<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>sweetsmelon博客</title>
    <description></description>
    <link>/</link>
    <atom:link href="/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Tue, 11 Sep 2018 14:25:36 +0800</pubDate>
    <lastBuildDate>Tue, 11 Sep 2018 14:25:36 +0800</lastBuildDate>
    <generator>Jekyll v3.5.1</generator>
    
      <item>
        <title>go gRPC 初体验</title>
        <description>&lt;h3 id=&quot;安装-grpc&quot;&gt;安装 gRPC&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;go get google.golang.org/grpc&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;安装-protocal-环境&quot;&gt;安装 protocal 环境&lt;/h3&gt;

&lt;p&gt;从 &lt;code class=&quot;highlighter-rouge&quot;&gt;https://github.com/google/protobuf/releases&lt;/code&gt;下载安装包，例如：protobuf-cpp-3.6.1.zip，解压后&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;./configure
make &amp;amp;&amp;amp; make install&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;等待至安装完成&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;zhangqiangdeMac-mini:protobuf-3.6.1 zhqmac$ protoc --version&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;提示版本信息，表示安装成功&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;libprotoc 3.6.1&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;goland-安装-protoc-插件&quot;&gt;goland 安装 protoc 插件&lt;/h3&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;插件名&lt;/th&gt;
      &lt;th&gt;用途&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Protobuf Support&lt;/td&gt;
      &lt;td&gt;protoc文件打开工具&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;ClangFormatIJ&lt;/td&gt;
      &lt;td&gt;protoc 代码提示工具&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;img src=&quot;http://otwxtrtn9.bkt.clouddn.com/goland%20%E5%AE%89%E8%A3%85%20protoc%20%E6%8F%92%E4%BB%B6.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;安装golang-protoc-插件&quot;&gt;安装GoLang protoc 插件&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;go get -a github.com/golang/protobuf/protoc-gen-go&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;定义service&quot;&gt;定义Service&lt;/h3&gt;

&lt;p&gt;一个RPC service就是一个能够通过参数和返回值进行远程调用的method，我们可以简单地将它理解成一个函数。因为gRPC是通过将数据编码成protocal buffer来实现传输的。因此，我们通过protocal buffers interface definitioin language(IDL)来定义service method，同时将参数和返回值也定义成protocal buffer message类型。具体实现如下所示，包含下面代码的文件叫helloworld.proto：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;syntax = &quot;proto3&quot;;

option java_package = &quot;io.grpc.examples&quot;;

package helloworld;

// The greeter service definition.
service Greeter {
    // Sends a greeting
    rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
    string name = 1;
}

// The response message containing the greetings
message HelloReply {
    string message = 1;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;接着，根据上述定义的service，我们可以利用protocal buffer compiler ，即protoc生成相应的服务器端和客户端的GoLang代码。生成的代码中包含了客户端能够进行RPC的方法以及服务器端需要进行实现的接口&lt;/p&gt;

&lt;p&gt;假设现在所在的目录是$GOPATH/src/helloworld/helloworld，我们将通过如下命令生成gRPC对应的GoLang代码：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;protoc --go_out=plugins=grpc:. proto/helloworld.proto&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;此时，将在目录下生成helloworld.pb.go文件。&lt;/p&gt;

&lt;h3 id=&quot;servergo&quot;&gt;server.go&lt;/h3&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;package main

// server.go

import (
	&quot;log&quot;
	&quot;net&quot;

	&quot;golang.org/x/net/context&quot;
	&quot;google.golang.org/grpc&quot;
	pb &quot;zhqGo/gRPCDemo/proto&quot;
)

const (
	port = &quot;:50051&quot;
)

type server struct {}

func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
	return &amp;amp;pb.HelloReply{Message: &quot;Hello &quot; + in.Name}, nil
}

func main() {
	lis, err := net.Listen(&quot;tcp&quot;, port)
	if err != nil {
		log.Fatal(&quot;failed to listen: %v&quot;, err)
	}
	s := grpc.NewServer()
	pb.RegisterGreeterServer(s, &amp;amp;server{})
	s.Serve(lis)
}

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h3 id=&quot;clientgo&quot;&gt;client.go&lt;/h3&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;package main

//client.go

import (
	&quot;log&quot;
	&quot;os&quot;

	&quot;golang.org/x/net/context&quot;
	&quot;google.golang.org/grpc&quot;
	pb &quot;zhqGo/gRPCDemo/proto&quot;
)

const (
	address     = &quot;localhost:50051&quot;
	defaultName = &quot;world&quot;
)

func main() {
	conn, err := grpc.Dial(address, grpc.WithInsecure())
	if err != nil {
		log.Fatal(&quot;did not connect: %v&quot;, err)
	}
	defer conn.Close()
	c := pb.NewGreeterClient(conn)

	name := defaultName
	if len(os.Args) &amp;gt;1 {
		name = os.Args[1]
	}
	r, err := c.SayHello(context.Background(), &amp;amp;pb.HelloRequest{Name: name})
	if err != nil {
		log.Fatal(&quot;could not greet: %v&quot;, err)
	}
	log.Printf(&quot;Greeting: %s&quot;, r.Message)
}

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;最后运行：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;go run server.go&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;go run client.go&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;以上是&lt;code class=&quot;highlighter-rouge&quot;&gt;grpc&lt;/code&gt;官网给出的一个demo，如果跑通了，表示环境已经没有问题了，接下来给它改一改&lt;/p&gt;

&lt;h3 id=&quot;添加age字段&quot;&gt;添加age字段&lt;/h3&gt;

&lt;h4 id=&quot;一打开helloworldproto文件找到&quot;&gt;一、打开&lt;code class=&quot;highlighter-rouge&quot;&gt;helloworld.proto&lt;/code&gt;文件,找到&lt;/h4&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;message HelloRequest{
        string name = 1;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;请求体，暂且就这么说吧，反正就是在&lt;code class=&quot;highlighter-rouge&quot;&gt;client&lt;/code&gt;端调用时需要传入的参数,添加字段&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;     int32 age = 2;
 
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;一些说明:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;string、int32为字段的数据类型，还有其他数据类型，见下表&lt;/li&gt;
  &lt;li&gt;name = 1,age=2,其中”1,2”表示字段tag值，tag值不可以重复&lt;/li&gt;
&lt;/ol&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;.proto Type&lt;/th&gt;
      &lt;th&gt;Notes&lt;/th&gt;
      &lt;th&gt;C++ Type&lt;/th&gt;
      &lt;th&gt;Java Type&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;double&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;doubel&lt;/td&gt;
      &lt;td&gt;doubel&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;float&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;float&lt;/td&gt;
      &lt;td&gt;float&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;int32&lt;/td&gt;
      &lt;td&gt;Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead.&lt;/td&gt;
      &lt;td&gt;int32&lt;/td&gt;
      &lt;td&gt;int&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;int64&lt;/td&gt;
      &lt;td&gt;Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead.&lt;/td&gt;
      &lt;td&gt;int64&lt;/td&gt;
      &lt;td&gt;long&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;uint32&lt;/td&gt;
      &lt;td&gt;Uses variable-length encoding.&lt;/td&gt;
      &lt;td&gt;uint32&lt;/td&gt;
      &lt;td&gt;int&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;uint64&lt;/td&gt;
      &lt;td&gt;Uses variable-length encoding.&lt;/td&gt;
      &lt;td&gt;uint64&lt;/td&gt;
      &lt;td&gt;long&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;sint32&lt;/td&gt;
      &lt;td&gt;Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s.&lt;/td&gt;
      &lt;td&gt;int32&lt;/td&gt;
      &lt;td&gt;int&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;sint64&lt;/td&gt;
      &lt;td&gt;Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s.&lt;/td&gt;
      &lt;td&gt;int64&lt;/td&gt;
      &lt;td&gt;long&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;fixed32&lt;/td&gt;
      &lt;td&gt;Always four bytes. More efficient than uint32 if values are often greater than 2^28.&lt;/td&gt;
      &lt;td&gt;uint32&lt;/td&gt;
      &lt;td&gt;int&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;fixed64&lt;/td&gt;
      &lt;td&gt;Always eight bytes. More efficient than uint64 if values are often greater than 2^56.&lt;/td&gt;
      &lt;td&gt;uint64&lt;/td&gt;
      &lt;td&gt;long&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;sfixed32&lt;/td&gt;
      &lt;td&gt;Always four bytes.&lt;/td&gt;
      &lt;td&gt;int32&lt;/td&gt;
      &lt;td&gt;int&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;sfixed64&lt;/td&gt;
      &lt;td&gt;Always eight bytes.&lt;/td&gt;
      &lt;td&gt;int64&lt;/td&gt;
      &lt;td&gt;long&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;bool&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;bool&lt;/td&gt;
      &lt;td&gt;boolean&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;string&lt;/td&gt;
      &lt;td&gt;A string must always contain UTF-8 encoded or 7-bit ASCII text.&lt;/td&gt;
      &lt;td&gt;string&lt;/td&gt;
      &lt;td&gt;string&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;bytes&lt;/td&gt;
      &lt;td&gt;May contain any arbitrary sequence of bytes.&lt;/td&gt;
      &lt;td&gt;string&lt;/td&gt;
      &lt;td&gt;ByteString&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h4 id=&quot;二添加响应字段&quot;&gt;二、添加响应字段&lt;/h4&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;message HelloReply {
    string message = 1;
    //age字段
    int32 ageMessage = 2;

}

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;在当前工程目录下执行命令,将重新生成&lt;code class=&quot;highlighter-rouge&quot;&gt;helloworld.pb.go&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;protoc –go_out=plugins=grpc:. proto/helloworld.proto&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;打开&lt;code class=&quot;highlighter-rouge&quot;&gt;helloworld.pb.go&lt;/code&gt;,可以看到一些变化&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;HelloRequest&lt;/code&gt;结构体中多了&lt;code class=&quot;highlighter-rouge&quot;&gt;Age&lt;/code&gt;字段和&lt;code class=&quot;highlighter-rouge&quot;&gt;func (m *HelloRequest) GetAge() int32 &lt;/code&gt;方法&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;HelloReply&lt;/code&gt;结构体中多了&lt;code class=&quot;highlighter-rouge&quot;&gt;AgeMessage&lt;/code&gt;字段和&lt;code class=&quot;highlighter-rouge&quot;&gt;func (m *HelloReply) GetAgeMessage() int32 &lt;/code&gt;方法&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h4 id=&quot;三调用&quot;&gt;三、调用&lt;/h4&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;client.go&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;替换下面代码&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;	r, err := c.SayHello(context.Background(), &amp;amp;pb.HelloRequest{Name: name,Age:25})
	if err != nil {
		log.Fatal(&quot;could not greet: %v&quot;, err)
	}
	log.Printf(&quot;Greeting: %s,%v&quot;, r.Message,r.AgeMessage)


&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;server.go&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;替换下面代码&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
	return &amp;amp;pb.HelloReply{Message:&quot;123&quot;,AgeMessage:in.Age}, nil
}

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;执行：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;go run server.go
go run client.go&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;可以看到有age输出。&lt;/p&gt;

&lt;h3 id=&quot;protobuf-repeated&quot;&gt;protobuf repeated&lt;/h3&gt;

&lt;p&gt;message中通过&lt;code class=&quot;highlighter-rouge&quot;&gt;repeated&lt;/code&gt;关键字+数据类型，声明一个数组，如声明一个int32的数组&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;repeated int32 array = 3
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;注：在&lt;code class=&quot;highlighter-rouge&quot;&gt;go&lt;/code&gt;调用方法时，repeated声明的字段为切片类型&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;packagetrue&quot;&gt;package==true&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;package&lt;/code&gt;用于修饰 ``repeated` 声明的字段&lt;/p&gt;

&lt;p&gt;1.作用&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://otwxtrtn9.bkt.clouddn.com/package==true11.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://otwxtrtn9.bkt.clouddn.com/package==true12.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;2.只能用于基本数据类型&lt;/p&gt;

&lt;h3 id=&quot;添加方法&quot;&gt;添加方法&lt;/h3&gt;

&lt;p&gt;一、声明方法&lt;/p&gt;

&lt;p&gt;打开&lt;code class=&quot;highlighter-rouge&quot;&gt;helloworld.proto&lt;/code&gt;文件&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;service Greeter {
    // Sends a greeting
    rpc SayHello (HelloRequest) returns (HelloReply) {}
    //添加ZHQTest方法
    //形参 TestRequest对象
    //返回值 TestReply对象
    rpc ZHQTest (TestRequest) returns (TestReply){}
}
message TestRequest {
    repeated string users = 1 ;
}
message TestReply{
    repeated string users = 1 ;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;在当前工程目录下执行命令,将重新生成&lt;code class=&quot;highlighter-rouge&quot;&gt;helloworld.pb.go&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;protoc –go_out=plugins=grpc:. proto/helloworld.proto&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;二、调用方法&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;server.go&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;//方法名必须与Greeter声明的方法名一致
func (s *server)ZHQTest(ctx context.Context, in *pb.TestRequest) (*pb.TestReply, error) {

	return &amp;amp;pb.TestReply{Users:in.Users},nil
}

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;client.go&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;func myRPCRequest(c pb.GreeterClient)  {
	users := []string{&quot;zhq&quot;, &quot;sweetsmelon&quot;}
	r, err := c.ZHQTest(context.Background(), &amp;amp;pb.TestRequest{Users: users})
	if err != nil {
		log.Fatal(&quot;could not greet: %v&quot;, err)
	}
	log.Printf(&quot;users is:%v,%s&quot;, r.Users[0],r.Users[1])
}


&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;执行：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;go run server.go&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;go run client.go&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;demo下载-httpsgithubcomsweetmegangrpcdemo&quot;&gt;demo下载 https://github.com/sweetMegan/gRPCDemo&lt;/h3&gt;

&lt;h3 id=&quot;参考链接&quot;&gt;参考链接：&lt;/h3&gt;

&lt;p&gt;https://www.cnblogs.com/YaoDD/p/5504881.html&lt;/p&gt;

&lt;p&gt;https://blog.csdn.net/weiwangchao_/article/details/16797763&lt;/p&gt;

&lt;p&gt;https://www.jianshu.com/p/30ef9b3780d9&lt;/p&gt;

</description>
        <pubDate>Fri, 07 Sep 2018 00:00:00 +0800</pubDate>
        <link>/2018/09/07/go-gRPC-%E5%88%9D%E4%BD%93%E9%AA%8C/</link>
        <guid isPermaLink="true">/2018/09/07/go-gRPC-%E5%88%9D%E4%BD%93%E9%AA%8C/</guid>
        
        <category>Go</category>
        
        
      </item>
    
      <item>
        <title>jwt 初体验</title>
        <description>&lt;h3 id=&quot;简介&quot;&gt;简介&lt;/h3&gt;

&lt;p&gt;1、什么是&lt;code class=&quot;highlighter-rouge&quot;&gt;JWT&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;JWT&lt;/code&gt;是&lt;code class=&quot;highlighter-rouge&quot;&gt;json web token&lt;/code&gt;缩写。它将用户信息加密
到&lt;code class=&quot;highlighter-rouge&quot;&gt;token&lt;/code&gt;里，服务器不保存任何用户信息。服务器通过使用保存的密钥验证&lt;code class=&quot;highlighter-rouge&quot;&gt;token&lt;/code&gt;的正确性，只要正确即通过验证。&lt;/p&gt;

&lt;p&gt;2、&lt;code class=&quot;highlighter-rouge&quot;&gt;JWT&lt;/code&gt;结构&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;JWT&lt;/code&gt;包含三部分:Header（头部）、Payload（负载）、Signature（签名）&lt;/p&gt;

&lt;p&gt;最终生成的&lt;code class=&quot;highlighter-rouge&quot;&gt;JWT&lt;/code&gt;样式:&lt;code class=&quot;highlighter-rouge&quot;&gt;base64URLEncoding(Header).&lt;/code&gt;+&lt;code class=&quot;highlighter-rouge&quot;&gt;base64URLEncoding(Payload).&lt;/code&gt;+&lt;code class=&quot;highlighter-rouge&quot;&gt;HMAC-SHA256(base64URLEncoding(Header).+base64URLEncoding(Payload).+密钥)&lt;/code&gt;&lt;/p&gt;

&lt;h4 id=&quot;header&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Header&lt;/code&gt;&lt;/h4&gt;

&lt;p&gt;一般保存两部分信息：&lt;code class=&quot;highlighter-rouge&quot;&gt;token&lt;/code&gt;类型和加密算法&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;alg&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;HS256&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;typ&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;JWT&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;经过Base64Url编码后作为JWT结构的第一部分。&lt;/p&gt;

&lt;h4 id=&quot;payload&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Payload&lt;/code&gt;&lt;/h4&gt;

&lt;p&gt;Token的第二部分是负载，它包含了claim， Claim是一些实体（通常指的用户）的状态和额外的元数据，有三种类型的claim：reserved, public 和 private.Reserved claims: 这些claim是JWT预先定义的，在JWT中并不会强制使用它们，而是推荐使用，常用的有 iss（签发者）,exp（过期时间戳）, sub（面向的用户）, aud（接收方）, iat（签发时间）。 Public claims：根据需要定义自己的字段，注意应该避免冲突 Private claims：这些是自定义的字段，可以用来在双方之间交换信息&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 
    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;sub&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;1234567890&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; 
    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;John Doe&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;admin&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
 &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
 
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;经过Base64Url编码后作为JWT结构的第二部分。&lt;/p&gt;

&lt;h4 id=&quot;signature&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Signature&lt;/code&gt;&lt;/h4&gt;

&lt;p&gt;创建签名需要使用编码后的header和payload以及一个秘钥，使用header中指定签名算法进行签名。例如如果希望使用HMAC SHA256算法，那么签名应该使用下列方式创建： HMACSHA256( base64UrlEncode(header) + “.” + base64UrlEncode(payload), secret) 签名用于验证消息的发送者以及消息是没有经过篡改的。 完整的JWT 完整的JWT格式的输出是以. 分隔的三段Base64编码，与SAML等基于XML的标准相比，JWT在HTTP和HTML环境中更容易传递。 下列的JWT展示了一个完整的JWT格式，它拼接了之前的Header， Payload以及秘钥签名：&lt;/p&gt;

&lt;p&gt;&amp;lt;font color=red&amp;gt;eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.&amp;lt;/font&amp;gt;&amp;lt;font color=purple&amp;gt;eyJleHAiOjE1MzU5NDgyNzMsImlhdCI6MTUzNTk0NDY3MywidXNlcm5hbWUiOiJzd2VldHNtZWxvbiJ9.&amp;lt;/font&amp;gt;&amp;lt;font color=blue&amp;gt;cfyaGsbVrHalZik1HPPqZAttk3sjM7XY-mBhdmCSis8&amp;lt;/font&amp;gt;&lt;/p&gt;

&lt;h4 id=&quot;如何使用jwt&quot;&gt;如何使用JWT&lt;/h4&gt;

&lt;p&gt;使用场景：身份鉴定、信息交换&lt;/p&gt;

&lt;p&gt;以身份鉴定为例:&lt;/p&gt;

&lt;p&gt;在身份鉴定的实现中，传统方法是在服务端存储一个session，给客户端返回一个cookie，而使用JWT之后，当用户使用它的认证信息登陆系统之后，会返回给用户一个JWT，用户只需要本地保存该token（通常使用local storage，也可以使用cookie）即可。 当用户希望访问一个受保护的路由或者资源的时候，通常应该在Authorization头部使用Bearer模式添加JWT，其内容看起来是下面这样：Authorization: Bearer &lt;token&gt;
因为用户的状态在服务端的内存中是不存储的，所以这是一种无状态的认证机制。服务端的保护路由将会检查请求头Authorization中的JWT信息，如果合法，则允许用户的行为。由于JWT是自包含的，因此减少了需要查询数据库的需要。 JWT的这些特性使得我们可以完全依赖其无状态的特性提供数据API服务，甚至是创建一个下载流服务。因为JWT并不使用Cookie的，所以你可以使用任何域名提供你的API服务而不需要担心跨域资源共享问题（CORS）。 下面的序列图展示了该过程：&lt;/token&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://segmentfault.com/image?src=http://source.aicode.cc/markdown/jwt-diagram.png&amp;amp;objectId=1190000005047525&amp;amp;token=fc83f4c0cf107cabeafd6a449cd49762&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;go-实现myjwt&quot;&gt;go 实现myJWT&lt;/h3&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;package main

import (
	&quot;time&quot;
	&quot;encoding/json&quot;
	&quot;encoding/base64&quot;
	&quot;fmt&quot;
	&quot;crypto/hmac&quot;
	&quot;crypto/sha256&quot;
	&quot;strings&quot;
)

const kScreateKey  = &quot;123&quot;
type Header struct {
	//加密方式
	Alg string
	//加密类型
	Typ string
}
type Payload struct {
	//过期时间
	Exp int64
	//签发时间
	Iat int64
	//用户名
	UserName string
}

type jwt struct {
	Header Header
	Payload Payload
	Signature string
}

func main() {
	token := createToken()
	//token := &quot;eyJBbGciOiJIUzI1NiIsIlR5cCI6IkpXVCJ9.eyJFeHAiOjE1MzU5NDk5MjUsIklhdCI6MTUzNTk0OTkyMywiVXNlck5hbWUiOiJzd2VldHNtZWxvbiJ9.C6XqHbR3Td9PhgTofVGlLnQ6PAab2pHm9T7yj_NtoQs&quot;
	verifyToken(token)
}
//创建token
func createToken() string {

	//1、创建header
	header := Header{&quot;HS256&quot;,&quot;JWT&quot;}
	//2、创建JWT第一段 header的base64编码
	headerBytes,err := json.Marshal(header)
	fmt.Println(err)
	header_Base64 := base64.URLEncoding.EncodeToString(headerBytes)
	//3、创建payload
	payload := Payload{}
	//token两秒后过期
	payload.Exp = time.Now().Add(time.Second*time.Duration(2)).Unix()
	payload.Iat = time.Now().Unix()
	payload.UserName = &quot;sweetsmelon&quot;
	//4、创建JWT第二段 payload的base64编码
	payloadBytes,_:= json.Marshal(payload)
	payload_Base64 := base64.URLEncoding.EncodeToString(payloadBytes)

	//5、拼接header和payload
	hpString := fmt.Sprintf(&quot;%s.%s&quot;,header_Base64,payload_Base64)
	//6、生成签名
	signatureStr := createSianature(hpString)
	token := fmt.Sprintf(&quot;%s.%s&quot;,hpString,signatureStr)
	fmt.Println(token)

	return token
}
func createSianature(hpString string) string  {
	hasher := hmac.New(sha256.New,[]byte(kScreateKey))
	hasher.Write([]byte(hpString))
	signatureStr :=strings.TrimRight(base64.URLEncoding.EncodeToString(hasher.Sum(nil)), &quot;=&quot;)
	return signatureStr
}
func verifyToken(token string)  {
	//1、根据&quot;.&quot;拆分token
	jwt := strings.Split(token,&quot;.&quot;)
	//2、解析header
	headerStr := jwt[0]
	headerBytes,_ := base64.URLEncoding.DecodeString(headerStr)
	header := Header{}
	json.Unmarshal(headerBytes,&amp;amp;header)
	//3、解析payload
	payloadStr := jwt[1]
	payloadBytes,_ := base64.URLEncoding.DecodeString(payloadStr)
	payload := Payload{}
	json.Unmarshal(payloadBytes,&amp;amp;payload)
	//4、验证签名是否有效
	hpString := fmt.Sprintf(&quot;%s.%s&quot;,headerStr,payloadStr)
	signatureString := createSianature(hpString)
	if signatureString == jwt[2] {
		if header.Alg == &quot;HS256&quot; &amp;amp;&amp;amp; header.Typ == &quot;JWT&quot; {
			//3、判断是否过期
			if payload.Exp &amp;lt; time.Now().Unix() {
				fmt.Println(&quot;验证码已失效&quot;)
			}else {
				fmt.Println(&quot;验证成功 username=&quot;,payload.UserName)
			}
		}else {
			fmt.Println(&quot;验证header失败&quot;)
		}
	}else {
		fmt.Println(&quot;验证签名失败&quot;)
	}
}

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h3 id=&quot;使用-jwt-go&quot;&gt;使用 jwt-go&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;jwt-go&lt;/code&gt;是一个封装好的&lt;code class=&quot;highlighter-rouge&quot;&gt;go&lt;/code&gt;语言版的&lt;code class=&quot;highlighter-rouge&quot;&gt;jwt&lt;/code&gt;工具&lt;/p&gt;

&lt;h4 id=&quot;安装jwt-go&quot;&gt;安装&lt;code class=&quot;highlighter-rouge&quot;&gt;jwt-go&lt;/code&gt;&lt;/h4&gt;

&lt;blockquote&gt;
  &lt;p&gt;go get github.com/dgrijalva/jwt-go&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4 id=&quot;使用&quot;&gt;使用&lt;/h4&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;package main

import (
	&quot;time&quot;
	&quot;github.com/dgrijalva/jwt-go&quot;
	&quot;reflect&quot;
	&quot;fmt&quot;
)
const kSecretKey = &quot;123456&quot;
func main() {
	createToken()
}
func createToken() {
	token := jwt.New(jwt.SigningMethodHS256)
	claims := make(jwt.MapClaims)
	claims[&quot;exp&quot;] = time.Now().Add(time.Hour * time.Duration(1)).Unix()
	claims[&quot;iat&quot;] = time.Now().Unix()
	//自定义属性username
	claims[&quot;username&quot;] = &quot;sweetsmelon&quot;
	token.Claims = claims

	tokenString, err := token.SignedString([]byte(kSecretKey))
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println(tokenString)
	veiftyToken(tokenString)
}
func veiftyToken(tokenStr string)  {
	token, err := jwt.Parse(tokenStr, func(token *jwt.Token) (interface{}, error) {
		return []byte(kSecretKey), nil
	})
	//通过映射，获取自定义的Claims属性
	switch token.Claims.(type) {
	case jwt.MapClaims:
		{
			v := reflect.ValueOf(token.Claims)
			v2 := v.Interface().(jwt.MapClaims)
			fmt.Println(v2[&quot;username&quot;])
			if err != nil {
				fmt.Println(&quot;验证失败&quot;)
			} else {
				if token.Valid {
					fmt.Println(&quot;验证成功&quot;)
				} else {
					fmt.Println(&quot;验证码失效&quot;)
				}
			}
			return
		}
	}
	fmt.Println(&quot;token格式错误，验证失败&quot;)
}

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;参考资料：&lt;/p&gt;

&lt;p&gt;https://jwt.io/introduction/&lt;/p&gt;

&lt;p&gt;https://blog.csdn.net/qq_40081976/article/details/79046825&lt;/p&gt;

&lt;p&gt;http://blog.51cto.com/xwandrew/2050973&lt;/p&gt;

</description>
        <pubDate>Sat, 01 Sep 2018 00:00:00 +0800</pubDate>
        <link>/2018/09/01/jwt-%E5%88%9D%E4%BD%93%E9%AA%8C/</link>
        <guid isPermaLink="true">/2018/09/01/jwt-%E5%88%9D%E4%BD%93%E9%AA%8C/</guid>
        
        <category>Go</category>
        
        
      </item>
    
      <item>
        <title>go iris 实现GET、POST、PUT接口</title>
        <description>&lt;h3 id=&quot;iris自称是go语言中所有web框架最快的它的特点如下&quot;&gt;Iris自称是Go语言中所有Web框架最快的，它的特点如下：&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;聚焦高性能&lt;/li&gt;
  &lt;li&gt;健壮的静态路由支持和通配符子域名支持。&lt;/li&gt;
  &lt;li&gt;视图系统支持超过5以上模板&lt;/li&gt;
  &lt;li&gt;支持定制事件的高可扩展性Websocket API&lt;/li&gt;
  &lt;li&gt;带有GC, 内存 &amp;amp; redis 提供支持的会话&lt;/li&gt;
  &lt;li&gt;方便的中间件和插件&lt;/li&gt;
  &lt;li&gt;完整 REST API&lt;/li&gt;
  &lt;li&gt;能定制 HTTP 错误&lt;/li&gt;
  &lt;li&gt;Typescript编译器 + 基于浏览器的编辑器&lt;/li&gt;
  &lt;li&gt;内容 negotiation &amp;amp; streaming&lt;/li&gt;
  &lt;li&gt;传送层安全性&lt;/li&gt;
  &lt;li&gt;源码改变后自动加载&lt;/li&gt;
  &lt;li&gt;OAuth, OAuth2 支持27+ API providers&lt;/li&gt;
  &lt;li&gt;JSON Web Tokens&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;使用-iris-实现-rest-api&quot;&gt;使用 iris 实现 REST API&lt;/h3&gt;

&lt;p&gt;安装&lt;code class=&quot;highlighter-rouge&quot;&gt;Iris&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;go get github.com/kataras/iris&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;一、&lt;code class=&quot;highlighter-rouge&quot;&gt;GET&lt;/code&gt;&lt;/p&gt;

&lt;h4 id=&quot;1不需要传参的get接口&quot;&gt;1.不需要传参的&lt;code class=&quot;highlighter-rouge&quot;&gt;GET&lt;/code&gt;接口&lt;/h4&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;package main

import &quot;github.com/kataras/iris&quot;

func main() {
	//获取一个iris对象
	app := iris.New()
	//绑定路由/login，访问后输出LOGIN
	app.Handle(&quot;GET&quot;,&quot;/login&quot;, func(ctx iris.Context) {
		ctx.JSON(&quot;LOGIN&quot;)
	})
	//在8080端口启动服务
	app.Run(iris.Addr(&quot;:8080&quot;))
}

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;执行代码,浏览器访问&lt;code class=&quot;highlighter-rouge&quot;&gt;http://localhost:8080/login&lt;/code&gt;,如果没有问题，窗口将会显示&lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;LOGIN&quot;&lt;/code&gt;&lt;/p&gt;

&lt;h4 id=&quot;2需要传参的get接口&quot;&gt;2.需要传参的&lt;code class=&quot;highlighter-rouge&quot;&gt;GET&lt;/code&gt;接口&lt;/h4&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;package main

import (
	&quot;github.com/kataras/iris&quot;
	&quot;fmt&quot;
)

func main() {
	//获取一个iris对象
	app := iris.New()
	//绑定路由/login/{用户名}/{密码}，访问后输出用户名，密码
	app.Handle(&quot;GET&quot;, &quot;/login/{username}/{password}&quot;, func(ctx iris.Context) {
		//获取参数值
		username := ctx.Params().Get(&quot;username&quot;)
		fmt.Println(username)
		password := ctx.Params().Get(&quot;password&quot;)
		fmt.Println(password)
		data := &amp;amp;struct {
			Username string
			Password string
		}{
			Username: username,
			Password: password,
		}
		ctx.JSON(data)
	})
	//在8080端口启动服务
	app.Run(iris.Addr(&quot;:8082&quot;))
}

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;iris&lt;/code&gt;中&lt;code class=&quot;highlighter-rouge&quot;&gt;GET&lt;/code&gt;接口传参采用&lt;code class=&quot;highlighter-rouge&quot;&gt;&lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;&lt;/code&gt;加&lt;code class=&quot;highlighter-rouge&quot;&gt;key&lt;/code&gt;值的方式
获取参数通过&lt;code class=&quot;highlighter-rouge&quot;&gt;ctx.Params().Get(key)&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;执行代码,浏览器访问&lt;code class=&quot;highlighter-rouge&quot;&gt;http://localhost:8082/login/zhq/123&lt;/code&gt;,如果没有问题，窗口将会显示&lt;code class=&quot;highlighter-rouge&quot;&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;password&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;123&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;username&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;zhq&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;二、&lt;code class=&quot;highlighter-rouge&quot;&gt;POST&lt;/code&gt;&lt;/p&gt;

&lt;h4 id=&quot;1接收的是字符串的处理&quot;&gt;1.接收的是字符串的处理&lt;/h4&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;func postAPI(app *iris.Application)  {
	//绑定路由/login/{用户名}/{密码}，访问后输出用户名，密码
	app.Handle(&quot;POST&quot;, &quot;/regist&quot;, func(ctx iris.Context) {
		//获取参数值
		username := ctx.FormValue(&quot;username&quot;)
		fmt.Println(&quot;==&quot;,username)
		password := ctx.FormValue(&quot;password&quot;)
		fmt.Println(password)
		data := &amp;amp;struct {
			Username string
			Password string
		}{
			Username: username,
			Password: password,
		}
		ctx.JSON(data)
	})
}

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;执行代码,打开终端执行&lt;code class=&quot;highlighter-rouge&quot;&gt;curl&lt;/code&gt;命令模拟&lt;code class=&quot;highlighter-rouge&quot;&gt;POST&lt;/code&gt;请求&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;zhangqiangdeMac-mini:~ zhqmac$ curl localhost:8080/regist -d ‘user=zhq&amp;amp;password=123456’ -X POST&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;输出结果:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;{“Username”:””,”Password”:”123456”}&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4 id=&quot;2接收的是json数据的处理&quot;&gt;2.接收的是&lt;code class=&quot;highlighter-rouge&quot;&gt;JSON&lt;/code&gt;数据的处理&lt;/h4&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;func postAPI_2(app *iris.Application)  {
	//绑定路由/login/{用户名}/{密码}，访问后输出用户名，密码
	app.Handle(&quot;POST&quot;, &quot;/regist&quot;, func(ctx iris.Context) {
		data := &amp;amp;struct {
			Username string `json:&quot;username&quot;`
			Password string `json:&quot;password&quot;`
		}{}
		//读取JSON数据
		ctx.ReadJSON(data)
		fmt.Println(data.Username,&quot;==&quot;,data.Password)
		ctx.JSON(data)
	})
}

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;执行代码,打开终端执行&lt;code class=&quot;highlighter-rouge&quot;&gt;curl&lt;/code&gt;命令模拟&lt;code class=&quot;highlighter-rouge&quot;&gt;POST&lt;/code&gt;请求&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;zhangqiangdeMac-mini:~ zhqmac$ curl -H “Content-Type:application/json” -X POST –data ‘{“username”:”zhq”,”password”:”123456”}’ http://localhost:8080/regist&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;运行结果:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;{“username”:”zhq”,”password”:”123456”}&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;三、&lt;code class=&quot;highlighter-rouge&quot;&gt;PUT&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;PUT&lt;/code&gt;和&lt;code class=&quot;highlighter-rouge&quot;&gt;POST&lt;/code&gt;在技术实现上是没有很大区别的。在上面的代码中将&lt;code class=&quot;highlighter-rouge&quot;&gt;POST&lt;/code&gt;改成&lt;code class=&quot;highlighter-rouge&quot;&gt;PUT&lt;/code&gt;即可，但在于协议上语义是很大区别的。&lt;/p&gt;

&lt;h4 id=&quot;1post&quot;&gt;1.&lt;code class=&quot;highlighter-rouge&quot;&gt;POST&lt;/code&gt;&lt;/h4&gt;

&lt;p&gt;用于提交请求，可以更新或者创建资源，是&lt;code class=&quot;highlighter-rouge&quot;&gt;非幂等的&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;举个例子，在我们的支付系统中，一个api的功能是创建收款金额二维码，它和金额相关，每个用户可以有多个二维码，如果连续调用则会创建新的二维码，这个时候就用POST&lt;/p&gt;

&lt;h4 id=&quot;2put&quot;&gt;2.&lt;code class=&quot;highlighter-rouge&quot;&gt;PUT&lt;/code&gt;&lt;/h4&gt;

&lt;p&gt;用于向指定的URI传送更新资源，&lt;code class=&quot;highlighter-rouge&quot;&gt;是幂等的&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;还是那个例子，用户的账户二维码只和用户关联，而且是一一对应的关系，此时这个api就可以用PUT，因为每次调用它，都将刷新用户账户二维码&lt;/p&gt;

&lt;h4 id=&quot;3什么是幂等&quot;&gt;3.什么是幂等&lt;/h4&gt;

&lt;p&gt;对于单目运算，如果一个运算对于在范围内的所有的一个数多次进行该运算所得的结果和进行一次该运算所得的结果是一样的，那么我们就称该运算是幂等的。比如绝对值运算就是一个例子，在实数集中，有abs(a) = abs(abs(a)) 。 　　
        对于双目运算，则要求当参与运算的两个值是等值的情况下，如果满足运算结果与参与运算的两个值相等，则称该运算幂等，如求两个数的最大值的函数，有在在实数集中幂等，即max(x,x) = x&lt;/p&gt;

&lt;h4 id=&quot;4当需要以更新的形式来修改某一具体资源的时候如何判断用put还是post呢&quot;&gt;4.当需要以更新的形式来修改某一具体资源的时候，如何判断用PUT还是POST呢？&lt;/h4&gt;

&lt;p&gt;很简单，如果该更新对应的URI多次调用的结果一致，则PUT&lt;/p&gt;

&lt;p&gt;比如更新某个blog文章，因为该文章具有单一的具体URI，所以每次更新提交相同的内容，结果都一致&lt;/p&gt;

&lt;p&gt;/blog/{document_id}/update&lt;/p&gt;

&lt;p&gt;在每次更新提交相同的内容，最终的结果不一致的时候，用POST&lt;/p&gt;

&lt;p&gt;举个很常见的例子，一个接口的功能是将当前余额减一个值，每次提交指定该值为100，接口如下&lt;/p&gt;

&lt;p&gt;/amount/deduction&lt;/p&gt;

&lt;p&gt;调用一次，你的余额-100，调用两次，余额-200&lt;/p&gt;

&lt;p&gt;这个时候就用POST&lt;/p&gt;

&lt;h3 id=&quot;demo下载-httpsgithubcomsweetmeganirisdemo&quot;&gt;demo下载 https://github.com/sweetMegan/irisDemo&lt;/h3&gt;

&lt;p&gt;参考资料：&lt;/p&gt;

&lt;p&gt;https://my.oschina.net/liujiest/blog/738992&lt;/p&gt;

&lt;p&gt;https://my.oschina.net/u/1263964/blog/268932&lt;/p&gt;

&lt;p&gt;https://baike.baidu.com/item/URI/2901761?fr=aladdin&lt;/p&gt;

</description>
        <pubDate>Sat, 01 Sep 2018 00:00:00 +0800</pubDate>
        <link>/2018/09/01/iris-%E5%AE%9E%E7%8E%B0GET-POST-PUT%E6%8E%A5%E5%8F%A3/</link>
        <guid isPermaLink="true">/2018/09/01/iris-%E5%AE%9E%E7%8E%B0GET-POST-PUT%E6%8E%A5%E5%8F%A3/</guid>
        
        <category>Go</category>
        
        
      </item>
    
      <item>
        <title>go 发送邮件</title>
        <description>&lt;h3 id=&quot;简述&quot;&gt;简述&lt;/h3&gt;

&lt;p&gt;Go 提供了一个 smtp（Simple Mail Transfer Protocol - 简单邮件传输协议）库作为其网络包的一部分，“net/smtp”公开了一些可立即使用的有用功能。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Gomail&lt;/code&gt; - 一个比较成熟的第三方包，提供了一个快速、简单的解决方案，可以轻松地发送电子邮件。&lt;/p&gt;

&lt;h3 id=&quot;smpt&quot;&gt;SMPT&lt;/h3&gt;

&lt;p&gt;SMTP（Simple Mail Transfer Protocol）即简单邮件传输协议,它是一组用于由源地址到目的地址传送邮件的规则，由它来控制信件的中转方式。SMTP协议属于TCP/IP协议簇，它帮助每台计算机在发送或中转信件时找到下一个目的地。通过SMTP协议所指定的服务器,就可以把E-mail寄到收信人的服务器上了，整个过程只要几分钟。SMTP服务器则是遵循SMTP协议的发送邮件服务器，用来发送或中转发出的电子邮件。&lt;/p&gt;

&lt;h3 id=&quot;实例&quot;&gt;实例&lt;/h3&gt;

&lt;h4 id=&quot;1导入gomail&quot;&gt;1.导入&lt;code class=&quot;highlighter-rouge&quot;&gt;gomail&lt;/code&gt;&lt;/h4&gt;

&lt;blockquote&gt;
  &lt;p&gt;go get “github.com/go-gomail/gomail”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4 id=&quot;2demo&quot;&gt;2.demo&lt;/h4&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;package main

import (
	&quot;bytes&quot;
	&quot;fmt&quot;
	&quot;github.com/go-gomail/gomail&quot;
	&quot;html/template&quot;
)

func main() {
	m := gomail.NewMessage()
	//发件人邮箱
	fromEmail := &quot;123456@qq.com&quot;

	//发件人昵称
	fromName := &quot;zhq&quot;
	m.SetAddressHeader(&quot;From&quot;, fromEmail, fromName) // 发件人
	//收件人邮箱
	toEmail := &quot;67890@163.com&quot;

	m.SetHeader(&quot;To&quot;, // 收件人
		m.FormatAddress(toEmail, &quot;&quot;),
	)
	m.SetHeader(&quot;Subject&quot;, &quot;发送邮件测试&quot;)            // 主题
	//初始化模板
	emailTemplate := `&amp;lt;div&amp;gt;&amp;lt;/div&amp;gt;`
	t, err := template.New(&quot;mail summary template&quot;).Parse(emailTemplate)
	if err != nil {
		fmt.Println(err)
	}
	buffer := new(bytes.Buffer)
	data := &amp;amp;struct{
		Vcode string
	}{
		Vcode:&quot;hello world&quot;,
	}
	//动态配置模板
	t.Execute(buffer,data)
	// 正文
	m.SetBody(&quot;text/html&quot;, buffer.String())
	//发件人邮箱授权码
	fromEmailPassword := &quot;123&quot;
	d := gomail.NewDialer(&quot;smtp.qq.com&quot;, 465, fromEmail, fromEmailPassword)
	if err := d.DialAndSend(m); err != nil {
		fmt.Println(err)
	}
	fmt.Println(&quot;邮件已发送&quot;)
}


&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;ps:需要注意的是 d := gomail.NewDialer(“smtp.qq.com”, 465, fromEmail, fromEmailPassword) 中 &lt;code class=&quot;highlighter-rouge&quot;&gt;fromEmailPassword&lt;/code&gt;并非邮箱密码，而是授权码&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;执行代码，如果输出&lt;code class=&quot;highlighter-rouge&quot;&gt;邮件已发送&lt;/code&gt;表示邮件发送成功，查看收件人邮箱即可&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://otwxtrtn9.bkt.clouddn.com/go%E5%8F%91%E9%80%81%E9%82%AE%E4%BB%B6.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;3获取授权码以qq邮箱为例&quot;&gt;3.获取授权码,以qq邮箱为例&lt;/h4&gt;

&lt;p&gt;进入邮箱 -&amp;gt; 设置 -&amp;gt; 账户 -&amp;gt; POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务 -&amp;gt; 开启POP3/SMTP服务&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://otwxtrtn9.bkt.clouddn.com/qq%E9%82%AE%E7%AE%B1%E8%8E%B7%E5%8F%96%E6%8E%88%E6%9D%83%E7%A0%811.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://otwxtrtn9.bkt.clouddn.com/qq%E9%82%AE%E7%AE%B1%E8%8E%B7%E5%8F%96%E6%8E%88%E6%9D%83%E7%A0%812.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;常用邮箱&quot;&gt;常用邮箱&lt;/h3&gt;

&lt;p&gt;列举一些常用的邮箱，可以用来测试：&lt;/p&gt;

&lt;p&gt;QQ 邮箱 
POP3 服务器地址：qq.com（端口：995） 
SMTP 服务器地址：smtp.qq.com（端口：465/587）&lt;/p&gt;

&lt;p&gt;163 邮箱： 
POP3 服务器地址：pop.163.com（端口：110） 
SMTP 服务器地址：smtp.163.com（端口：25）&lt;/p&gt;

&lt;p&gt;126 邮箱： 
POP3 服务器地址：pop.126.com（端口：110） 
SMTP 服务器地址：smtp.126.com（端口：25）&lt;/p&gt;

&lt;h3 id=&quot;参考资料&quot;&gt;参考资料:&lt;/h3&gt;

&lt;p&gt;https://blog.csdn.net/liang19890820/article/details/53084638&lt;/p&gt;

&lt;p&gt;https://blog.csdn.net/wangshubo1989/article/details/70808989&lt;/p&gt;

</description>
        <pubDate>Sat, 01 Sep 2018 00:00:00 +0800</pubDate>
        <link>/2018/09/01/go%E5%8F%91%E9%80%81%E9%82%AE%E4%BB%B6/</link>
        <guid isPermaLink="true">/2018/09/01/go%E5%8F%91%E9%80%81%E9%82%AE%E4%BB%B6/</guid>
        
        <category>Go</category>
        
        
      </item>
    
      <item>
        <title>go test使用</title>
        <description>&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;go test&lt;/code&gt; 功能，提高了开发和测试的效率。&lt;/p&gt;

&lt;p&gt;下面例子的文件结构如下：&lt;/p&gt;

&lt;p&gt;goTest使用/add.go
goTest使用/test_add.go&lt;/p&gt;

&lt;h3 id=&quot;addgo&quot;&gt;add.go&lt;/h3&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;package goTest使用

import &quot;fmt&quot;

func Add(a, b int) int {
	return a+b
}
func What() {
fmt.Println(&quot;what ?&quot;)
}

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h3 id=&quot;add_testgo&quot;&gt;add_test.go&lt;/h3&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;package goTest使用

import (
	&quot;testing&quot;
	&quot;fmt&quot;
)

func TestAdd(t *testing.T) {
	r := Add(1,2)
	fmt.Println(r)

}
func TestWhat(t *testing.T) {
	What()
}

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;进入两个文件所在的目录下,如&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;zhangqiangdeMac-mini:Test zhqmac$ cd goTest使用/&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;执行 &lt;code class=&quot;highlighter-rouge&quot;&gt;go test&lt;/code&gt;命令&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;zhangqiangdeMac-mini:goTest使用 zhqmac$ go test&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;控制台输出:

3  //Add()结果
what ? //What()结果
PASS
ok      zhqGo/Test/goTest使用   0.007s

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h4 id=&quot;测试某个函数&quot;&gt;&lt;strong&gt;测试某个函数&lt;/strong&gt;&lt;/h4&gt;

&lt;p&gt;go test 命令，默认运行所有在目录下xxx_test.go 的测试文件 
而且只对以Testxxx(t tesing.T)的函数进行测试,要单测某个函数,如&lt;code class=&quot;highlighter-rouge&quot;&gt;Add()&lt;/code&gt;可以使用命令&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;go test -test.run Add&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;或&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;go test -test.run TestAdd&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4 id=&quot;测试单个文件&quot;&gt;&lt;strong&gt;测试单个文件&lt;/strong&gt;&lt;/h4&gt;

&lt;blockquote&gt;
  &lt;p&gt;go test -v add_test.go  add.go&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4 id=&quot;测试前初始化&quot;&gt;&lt;strong&gt;测试前初始化&lt;/strong&gt;&lt;/h4&gt;

&lt;p&gt;进行测试之前需要初始化操作(例如打开连接)，测试结束后，需要做清理工作(例如关闭连接)等等。这个时候就可以使用TestMain()。&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;package goTest使用

import (
	&quot;testing&quot;
	&quot;fmt&quot;
)

func TestAdd(t *testing.T) {
	r := Add(1,2)
	fmt.Println(r)

}
func TestWhat(t *testing.T) {
	What()
}
func TestMain(m *testing.M)  {
//初始化操作
	fmt.Println(&quot;begin&quot;)
	m.Run()
	fmt.Println(&quot;end&quot;)
}

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h3 id=&quot;参数说明&quot;&gt;参数说明&lt;/h3&gt;

&lt;h4 id=&quot;1运行参数&quot;&gt;&lt;strong&gt;1、运行参数&lt;/strong&gt;&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;go test 忽略log信息&lt;/li&gt;
  &lt;li&gt;go test -v 冗长模式， 打印实时的log信息&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;2测试过程控制&quot;&gt;&lt;strong&gt;2、测试过程控制&lt;/strong&gt;&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;t.Log&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;t.Logf&lt;/code&gt; // 正常信息&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;t.Error&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;t.Errorf&lt;/code&gt; // 测试失败信息&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;t.Fatal&lt;/code&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;t.Fatalf&lt;/code&gt; // 致命错误， 测试程序退出的信息&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;t.Fail&lt;/code&gt;   //当前测试标记为失败&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;t.Failed&lt;/code&gt; //查看失败标记&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;t.FailNow&lt;/code&gt;// 标记失败， 并终止当前测试函数的执行(需要注意的是，我们只能在运行测试函数的Goroutine中调用
       //t.FailNow方法，而不能在我们在测试代码创建出的Goroutine中调用它。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;t.Skip&lt;/code&gt; //调用t.Skip方法相当于先后对t.Log和t.SkipNow方法进行调用，而调用t.Skipf方法则相当于先后对             //t.Logf和t.Skip-Now方法进行调用。方法t.Skipped的结果值会告知我们当前的测试是否已被忽略。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;t.Parallel&lt;/code&gt; // 标记为可并行运算&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;参考资料&quot;&gt;参考资料&lt;/h3&gt;

&lt;p&gt;go test 初始化— TestMain的使用&lt;br /&gt;
https://www.cnblogs.com/lanyangsh/p/8685989.html&lt;/p&gt;

</description>
        <pubDate>Thu, 02 Aug 2018 00:00:00 +0800</pubDate>
        <link>/2018/08/02/go-test%E4%BD%BF%E7%94%A8/</link>
        <guid isPermaLink="true">/2018/08/02/go-test%E4%BD%BF%E7%94%A8/</guid>
        
        <category>Go</category>
        
        
      </item>
    
      <item>
        <title>go defer、panic、recover使用</title>
        <description>&lt;h3 id=&quot;defer&quot;&gt;defer&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;defer&lt;/code&gt;和&lt;code class=&quot;highlighter-rouge&quot;&gt;go&lt;/code&gt;一样都是&lt;code class=&quot;highlighter-rouge&quot;&gt;Go&lt;/code&gt;语言提供的关键字。&lt;code class=&quot;highlighter-rouge&quot;&gt;defer&lt;/code&gt;用于资源的释放，会在函数返回之前进行调用。&lt;/p&gt;

&lt;h4 id=&quot;多个defer调用顺序&quot;&gt;&lt;strong&gt;多个defer调用顺序&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;如果有多个&lt;code class=&quot;highlighter-rouge&quot;&gt;defer&lt;/code&gt;表达式，调用顺序类似于栈，越后面的&lt;code class=&quot;highlighter-rouge&quot;&gt;defer&lt;/code&gt;表达式越先被调用。&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;func f() {
	defer fmt.Println(&quot;defer 1&quot;)
	defer fmt.Println(&quot;defer 2&quot;)
	defer fmt.Println(&quot;defer 3&quot;)
}
执行结果为：
defer 3
defer 2
defer 1

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h4 id=&quot;defer函数形参的值&quot;&gt;&lt;strong&gt;defer函数形参的值&lt;/strong&gt;&lt;/h4&gt;

&lt;p&gt;如果&lt;code class=&quot;highlighter-rouge&quot;&gt;defer&lt;/code&gt;函数需要外界传参，形参的值，在&lt;code class=&quot;highlighter-rouge&quot;&gt;defer&lt;/code&gt;函数定义时形参就已经被赋值。&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;func f() {
	i := 1
	defer func(i int) {
		fmt.Println(&quot;defer &quot;, i)

	}(i) //i = 1
	i++
	defer func(i int) {
		fmt.Println(&quot;defer &quot;, i)
	}(i) //i = 2
	i++
	defer func(i int) {
		fmt.Println(&quot;defer &quot;, i)
	}(i)//i = 3
	i++
	defer func(i int) {
		fmt.Println(&quot;defer &quot;, i)
	}(i) //i = 4
}

执行结果:
defer  4
defer  3
defer  2
defer  1

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h4 id=&quot;deferreturn的执行顺序&quot;&gt;&lt;strong&gt;defer,return的执行顺序&lt;/strong&gt;&lt;/h4&gt;

&lt;p&gt;先看两段代码
代码1：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;func main() {
	fmt.Println(&quot;b return:&quot;, b()) // 打印结果为 b return: 2
}

func b() (i int) {
	defer func() {
		i++
		fmt.Println(&quot;b defer2:&quot;, i) // 打印结果为 b defer2: 2
	}()
	defer func() {
		i++
		fmt.Println(&quot;b defer1:&quot;, i) // 打印结果为 b defer1: 1
	}()
	return i // 或者直接 return 效果相同
}

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;代码2：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;func main() {
	fmt.Println(&quot;a return:&quot;, a()) // 打印结果为 a return: 0
}

func a() int {
	var i int
	defer func() {
		i++
		fmt.Println(&quot;a defer2:&quot;, i) // 打印结果为 a defer2: 2
	}()
	defer func() {
		i++
		fmt.Println(&quot;a defer1:&quot;, i) // 打印结果为 a defer1: 1
	}()
	return i
}

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;两段代码区别在于，a()声明了返回值,b()返回值是匿名的
代码执行结果；&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;a()返回值为 0
b()返回值为 2

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;依据结果，先来假设出结论（这是正确结论），帮助大家理解原因：&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;多个defer的执行顺序为“后进先出”；&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;所有函数在执行RET返回指令之前，都会先检查是否存在defer语句，若存在则先逆序调用defer语句进行收尾工作再退出返回；&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;匿名返回值是在return执行时被声明，有名返回值则是在函数声明的同时被声明，因此在defer语句中只能访问有名返回值，而不能直接访问匿名返回值；&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;return其实应该包含前后两个步骤：第一步是给返回值赋值（若为有名返回值则直接赋值，若为匿名返回值则先声明再赋值）；第二步是调用RET返回指令并传入返回值，而RET则会检查defer是否存在，若存在就先逆序插播defer语句，最后RET携带返回值退出函数；&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;‍‍因此，defer、return、返回值三者的执行顺序应该是：return最先给返回值赋值；接着defer开始执行一些收尾工作；最后RET指令携带返回值退出函数。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;验证结论&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;func main() {
	c:=c()
	fmt.Println(&quot;c return:&quot;, *c, c) // 打印结果为 c return: 2 0xc082008340
}

func c() *int {
	var i int
	defer func() {
		i++
		fmt.Println(&quot;c defer2:&quot;, i, &amp;amp;i) // 打印结果为 c defer2: 2 0xc082008340
	}()
	defer func() {
		i++
		fmt.Println(&quot;c defer1:&quot;, i, &amp;amp;i) // 打印结果为 c defer1: 1 0xc082008340
	}()
	return &amp;amp;i
}

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;虽然 &lt;code class=&quot;highlighter-rouge&quot;&gt;c() *int&lt;/code&gt; 的返回值没有被提前声明，但是由于&lt;code class=&quot;highlighter-rouge&quot;&gt; c() *int &lt;/code&gt;的返回值是指针变量，那么在&lt;code class=&quot;highlighter-rouge&quot;&gt;return&lt;/code&gt;将变量 i 的地址赋给返回值后，&lt;code class=&quot;highlighter-rouge&quot;&gt;defer&lt;/code&gt;再次修改了 i 在内存中的实际值，因此return调用RET退出函数时返回值虽然依旧是原来的指针地址，但是其指向的内存实际值已经被成功修改了。&lt;/p&gt;

&lt;p&gt;即，我们假设的结论是正确的！&lt;/p&gt;

&lt;h4 id=&quot;defer的作用域&quot;&gt;&lt;strong&gt;defer的作用域&lt;/strong&gt;&lt;/h4&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;defer只对当前协程有效（main可以看作是主协程）；&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;当panic发生时依然会执行当前（主）协程中已声明的defer，但如果所有defer都未调用recover()进行异常恢复，则会在执行完所有defer后引发整个进程崩溃；&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;主动调用os.Exit(int)退出进程时，已声明的defer将不再被执行。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;panicrecover&quot;&gt;panic、recover&lt;/h3&gt;

&lt;p&gt;Go语言不支持传统的 &lt;code class=&quot;highlighter-rouge&quot;&gt;try…catch…finally&lt;/code&gt; 这种异常，在&lt;code class=&quot;highlighter-rouge&quot;&gt;go&lt;/code&gt;中通过defer, panic, recover来处理异常。&lt;/p&gt;

&lt;h4 id=&quot;panic&quot;&gt;&lt;strong&gt;panic&lt;/strong&gt;&lt;/h4&gt;

&lt;p&gt;当&lt;code class=&quot;highlighter-rouge&quot;&gt;panic&lt;/code&gt;发生时依然会执行当前（主）协程中已声明的&lt;code class=&quot;highlighter-rouge&quot;&gt;defer&lt;/code&gt;，但如果所有&lt;code class=&quot;highlighter-rouge&quot;&gt;defer&lt;/code&gt;都未调用&lt;code class=&quot;highlighter-rouge&quot;&gt;recover()&lt;/code&gt;进行异常恢复，则会在执行完所有defer后引发整个进程崩溃；&lt;/p&gt;

&lt;p&gt;代码1：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;func main() {
	ch := make(chan int)
	go func() {
	defer func() {
		ch &amp;lt;- 1
	}()
	panic(&quot;ss&quot;)
	}()
	&amp;lt;-ch
	fmt.Println(&quot;main 执行结束&quot;)
}

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;当程序执行到&lt;code class=&quot;highlighter-rouge&quot;&gt;panic(&quot;ss&quot;)&lt;/code&gt;进程崩溃，&lt;code class=&quot;highlighter-rouge&quot;&gt;	fmt.Println(&quot;main 执行结束&quot;)&lt;/code&gt;可能会执行，也可能不会被执行。因为&lt;code class=&quot;highlighter-rouge&quot;&gt;defer&lt;/code&gt;执行完之后，主线程继续执行，可能这时进程并未崩溃。&lt;/p&gt;

&lt;p&gt;代码2：&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;	ch := make(chan int)
	go func() {
	defer func() {

		if err := recover(); err != nil {
			fmt.Println(&quot;recover&quot;)
			ch&amp;lt;-1

		}
	}()
	panic(&quot;ss&quot;)
	}()
	&amp;lt;-ch
	fmt.Println(&quot;main 执行结束&quot;)

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;通过&lt;code class=&quot;highlighter-rouge&quot;&gt;recover&lt;/code&gt;捕获异常，并对异常进行处理，程序正常运行&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;网络应答异常处理&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;type result struct {
	code int
	msg  string
}
func main() {
	
	defer func() {
		if res := recover(); res != nil {
			r, ok := res.(*result)
			if ok {
				fmt.Println(r.msg)
				fmt.Println(r.code)
			}
		}
	}()

	panic(&amp;amp;result{1, &quot;除数不能为0&quot;})

}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h3 id=&quot;参考资料&quot;&gt;参考资料&lt;/h3&gt;

&lt;p&gt;Golang中defer、return、返回值之间执行顺序的坑:&lt;br /&gt;https://my.oschina.net/henrylee2cn/blog/505535&lt;/p&gt;

</description>
        <pubDate>Thu, 02 Aug 2018 00:00:00 +0800</pubDate>
        <link>/2018/08/02/go-defer-panic-recover%E4%BD%BF%E7%94%A8/</link>
        <guid isPermaLink="true">/2018/08/02/go-defer-panic-recover%E4%BD%BF%E7%94%A8/</guid>
        
        <category>Go</category>
        
        
      </item>
    
      <item>
        <title>以太坊私网建立-通过创世区块来初始化区块链</title>
        <description>&lt;h3 id=&quot;一创建目录privatechain名字随便起&quot;&gt;一、创建目录&lt;code class=&quot;highlighter-rouge&quot;&gt;privatechain&lt;/code&gt;,名字随便起&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;zhangqiangdeMac-mini:~ zhqmac$  cd Desktop&lt;/p&gt;

  &lt;p&gt;zhangqiangdeMac-mini:Desktop zhqmac$ mkdir privatechain&lt;/p&gt;

  &lt;p&gt;zhangqiangdeMac-mini:Desktop zhqmac$ cd privatechain/&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;二创建创世块&quot;&gt;二、创建创世块&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;vi genesis.json&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;拷贝下面的内容&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;alloc&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;coinbase&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;0x0000000000000000000000000000000000000000&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;config&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;chainId&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;110&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;eip155Block&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;eip158Block&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;homesteadBlock&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;difficulty&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;0x20000&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;extraData&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;gasLimit&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;0x2fefd8&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;mixhash&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;0x0000000000000000000000000000000000000000000000000000000000000000&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;nonce&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;0x0000000000000042&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;parentHash&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;0x0000000000000000000000000000000000000000000000000000000000000000&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;timestamp&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;0x00&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;保存退出&lt;/p&gt;

&lt;p&gt;参数说明:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;参数&lt;/th&gt;
      &lt;th&gt;说明&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;mixhash&lt;/td&gt;
      &lt;td&gt;与nonce配合用户挖矿，由上一个区块的一部分生成的hash。&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;nonce&lt;/td&gt;
      &lt;td&gt;nonce就是一个64位随机数，用于挖矿&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;difficulty&lt;/td&gt;
      &lt;td&gt;设置当前区块的难度，如果难度过大，cpu挖矿就很难，这里设置较小难度&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;alloc&lt;/td&gt;
      &lt;td&gt;用来预置账号以及账号的以太币数量,因为私有链挖矿比较容易，所以我们不需要预置有币的账号，需要的时候自己创建即可以&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;coinbase&lt;/td&gt;
      &lt;td&gt;矿工的账号，随便填&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;timestamp&lt;/td&gt;
      &lt;td&gt;设置创世块的时间戳&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;parentHash&lt;/td&gt;
      &lt;td&gt;上一个区块的hash值，因为是创世块，所以这个值是0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;extraData&lt;/td&gt;
      &lt;td&gt;附加信息，随便填，可以填你的个性信息&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;gasLimit&lt;/td&gt;
      &lt;td&gt;该值设置对GAS的消耗总量限制，用来限制区块能包含的交易信息总和，因为我们是私链，所以随意写&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h3 id=&quot;三初始化区块链并且创建一个文件夹来存储区块数据&quot;&gt;三、初始化区块链，并且创建一个文件夹来存储区块数据&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;zhangqiangdeMac-mini:privatechain zhqmac$ geth init genesis.json --datadir blockchainData&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;四启动私有链节点&quot;&gt;四、启动私有链节点&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;geth --datadir blockchainData/ --networkid 110 --rpc console&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;如果出现&lt;code class=&quot;highlighter-rouge&quot;&gt;Welcome to the Geth JavaScript console!&lt;/code&gt;表示启动成功&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;参数&lt;/th&gt;
      &lt;th&gt;说明&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;identity&lt;/td&gt;
      &lt;td&gt;区块链的标示，随便填写，用于标示目前网络的名字&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;init&lt;/td&gt;
      &lt;td&gt;指定创世区块文件的位置，并创建初始块&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;datadir&lt;/td&gt;
      &lt;td&gt;设置当前区块链网络数据存放的位置&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;port&lt;/td&gt;
      &lt;td&gt;网络监听端口&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;rpc&lt;/td&gt;
      &lt;td&gt;启动rpc通信，可以进行智能合约的部署和调试&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;rpcapi&lt;/td&gt;
      &lt;td&gt;设置允许链接的rpc的客户端，一般为db,eth,net,web3&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;networkid&lt;/td&gt;
      &lt;td&gt;设置当前区块链的网络ID,用于区分不同的网络，是个数字，&lt;code class=&quot;highlighter-rouge&quot;&gt;1&lt;/code&gt;表示主网&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;console&lt;/td&gt;
      &lt;td&gt;启动命令行模式，可以在&lt;code class=&quot;highlighter-rouge&quot;&gt;geth&lt;/code&gt;中执行命令.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h3 id=&quot;五新建账户&quot;&gt;五、新建账户&lt;/h3&gt;

&lt;p&gt;查看所有账户&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;personal.listAccounts&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;返回为&lt;code class=&quot;highlighter-rouge&quot;&gt;[]&lt;/code&gt;,此时还没有账户&lt;/p&gt;

&lt;p&gt;新建账户&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;personal.newAccount(“123456”)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;123456&quot;&lt;/code&gt;为账户密码&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;personal.listAccounts&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;可以查看到刚才创建的钱包地址,在&lt;code class=&quot;highlighter-rouge&quot;&gt;blockchainData&lt;/code&gt;目录下的&lt;code class=&quot;highlighter-rouge&quot;&gt;keystore&lt;/code&gt;目录中多了一个keystore文件，里面保存的即是次账号的一些信息:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;address&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;0a778959e98293bdc1101ec211dbc3135be2b59a&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;crypto&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:{&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;cipher&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;aes-128-ctr&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;ciphertext&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;e6e5c00cd34e76f4d64b07f0de100f3a81cfbe8b11b3c7ffc52f7bdad6308a27&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;cipherparams&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:{&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;iv&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;498e15f6d7244bab299ff986e4155b2b&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;kdf&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;scrypt&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;kdfparams&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:{&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;dklen&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;n&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;262144&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;p&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;r&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;salt&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;5b0345890294ee5fc050b00e852963ce41cc60ae69ff86302d1a361498b09ba5&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;mac&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;2e7f9c197ed4dbb9c5c3888b28639f5d38767ab3bb3359f4b58dc195d9798f2f&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;a1b23559-f59e-44df-a32d-92f6682fd86f&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h3 id=&quot;六挖矿&quot;&gt;六、挖矿&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;查看账户余额&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;eth.getBalance(eth.coinbase)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;查询结果为0，&lt;code class=&quot;highlighter-rouge&quot;&gt;eth.coinbase&lt;/code&gt;矿工的账号，默认为&lt;code class=&quot;highlighter-rouge&quot;&gt;personal.listAccounts&lt;/code&gt;第一个账号&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;设置&lt;code class=&quot;highlighter-rouge&quot;&gt;coinbase&lt;/code&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;eth.setEtherbase(eth.accounts[0])&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;因为此时我们只有一个账号，如果想要设置其他账号为挖矿账号，可以新建一个账号，通过&lt;code class=&quot;highlighter-rouge&quot;&gt;eth.accounts[index]&lt;/code&gt;来设置，或者直接将地址填在这里&lt;code class=&quot;highlighter-rouge&quot;&gt;eth.setEtherbase(&quot;0x0a778959e98293bdc1101ec211dbc3135be2b59a&quot;)&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;开始挖矿&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;miner.start(1)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;1&lt;/code&gt;表示线程数&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://otwxtrtn9.bkt.clouddn.com/%E5%BC%80%E5%90%AF%E6%8C%96%E7%9F%BF.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如果卡在&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;INFO [07-24|00:48:46.606] Commit new mining work                   number=2 txs=0 uncles=0 elapsed=129.846µs
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;没有关系，等着就好，根据电脑性能不同，时间可长可短&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;停止挖矿&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;miner.stop()&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;输入可能被挖矿的输出截断，没关系，直接输完，回车就好&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;看下余额&lt;/strong&gt;
    &lt;blockquote&gt;
      &lt;p&gt;eth.getBalance(eth.coinbase)&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这时就应该有以太币了，单位为&lt;code class=&quot;highlighter-rouge&quot;&gt;wei&lt;/code&gt;&lt;/p&gt;

&lt;h3 id=&quot;七发送交易&quot;&gt;七、发送交易&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;创建一个账号&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;personal.newAccount(“123456”)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;解锁账号&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;personal.unlockAccount(eth.coinbase)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;输入密码&lt;/p&gt;

&lt;p&gt;如下输出表示解锁成功&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Unlock account 0x0a778959e98293bdc1101ec211dbc3135be2b59a
Passphrase: 
true
&amp;gt; 

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;注：解锁的账号，为了安全，一段时间后会自动上锁&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;转账&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;eth.sendTransaction({from:eth.coinbase,to:eth.accounts[1],value:100000000})&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;返回结果:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; &amp;gt; eth.sendTransaction({from:eth.coinbase,to:eth.accounts[1],value:100000000})
INFO [07-24|01:00:53.132] Submitted transaction                    fullhash=0xa0517620c64f43e50db46836449a18afcf749200aaa13e5ce8f17b2d86be7b4a recipient=0x19BD3Fe6E7Db24aDaA5Dc012a154A9a03dCCD1fE
&quot;0xa0517620c64f43e50db46836449a18afcf749200aaa13e5ce8f17b2d86be7b4a&quot;
 
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;0xa0517620c64f43e50db46836449a18afcf749200aaa13e5ce8f17b2d86be7b4a&lt;/code&gt;为交易hash&lt;/p&gt;

&lt;p&gt;或者&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;amount = web3.toWei(10,’ether’)
eth.sendTransaction({from:eth.coinbase,to:eth.accounts[1],value:amount})&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;查看&lt;code class=&quot;highlighter-rouge&quot;&gt;eth.accounts[1]&lt;/code&gt;的余额&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;eth.getBalance(eth.accounts[1])&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;余额还是&lt;code class=&quot;highlighter-rouge&quot;&gt;0&lt;/code&gt;,因为交易还没有被验证，需要矿工挖矿确认。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;miner.start()
miner.stop()&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;再查看余额&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;eth.getBalance(eth.accounts[1])&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;转账成功&lt;/p&gt;

&lt;h3 id=&quot;八查看交易&quot;&gt;八、查看交易&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;eth.getTransaction(“0xa0517620c64f43e50db46836449a18afcf749200aaa13e5ce8f17b2d86be7b4a”)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;可以查看交易信息。&lt;/p&gt;

&lt;h3 id=&quot;九查看当前区块总数&quot;&gt;九、查看当前区块总数&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;eth.blockNumber&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;十通过区块号查看区块&quot;&gt;十、通过区块号查看区块&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;eth.getBlock(6)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;十一交易池状态&quot;&gt;十一、交易池状态&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;txpool.status&lt;/p&gt;
&lt;/blockquote&gt;

</description>
        <pubDate>Sun, 15 Jul 2018 00:00:00 +0800</pubDate>
        <link>/2018/07/15/%E4%BB%A5%E5%A4%AA%E5%9D%8A%E7%A7%81%E7%BD%91%E5%BB%BA%E7%AB%8B-%E9%80%9A%E8%BF%87%E5%88%9B%E4%B8%96%E5%8C%BA%E5%9D%97%E6%9D%A5%E5%88%9D%E5%A7%8B%E5%8C%96%E5%8C%BA%E5%9D%97%E9%93%BE/</link>
        <guid isPermaLink="true">/2018/07/15/%E4%BB%A5%E5%A4%AA%E5%9D%8A%E7%A7%81%E7%BD%91%E5%BB%BA%E7%AB%8B-%E9%80%9A%E8%BF%87%E5%88%9B%E4%B8%96%E5%8C%BA%E5%9D%97%E6%9D%A5%E5%88%9D%E5%A7%8B%E5%8C%96%E5%8C%BA%E5%9D%97%E9%93%BE/</guid>
        
        <category>区块链</category>
        
        
      </item>
    
      <item>
        <title>Nodejs实现以太坊钱包</title>
        <description>&lt;h2 id=&quot;一创建项目&quot;&gt;一、创建项目&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;zhangqiangdeMac-mini:~ zhqmac$ mkdir myWallet&lt;/p&gt;

  &lt;p&gt;zhangqiangdeMac-mini:~ zhqmac$  cd myWallet&lt;/p&gt;

  &lt;p&gt;zhangqiangdeMac-mini:myWallet zhqmac$ npm init&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;将下面的依赖添加到生成的&lt;code class=&quot;highlighter-rouge&quot;&gt;package.json&lt;/code&gt;文件中&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; &quot;dependencies&quot;: {
    &quot;bignumber.js&quot;: &quot;^7.2.1&quot;,
    &quot;ejs&quot;: &quot;^2.6.1&quot;,
    &quot;koa&quot;: &quot;^2.5.2&quot;,
    &quot;koa-body&quot;: &quot;^4.0.4&quot;,
    &quot;koa-bodyparser&quot;: &quot;^4.2.1&quot;,
    &quot;koa-router&quot;: &quot;^7.4.0&quot;,
    &quot;koa-static&quot;: &quot;^5.0.0&quot;,
    &quot;koa-views&quot;: &quot;^6.1.4&quot;,
    &quot;web3&quot;: &quot;^1.0.0-beta.34&quot;
  }
  
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h4 id=&quot;安装依赖&quot;&gt;安装依赖&lt;/h4&gt;

&lt;blockquote&gt;
  &lt;p&gt;npm install&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;二初始化项目&quot;&gt;二、初始化项目&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;新建文件&lt;code class=&quot;highlighter-rouge&quot;&gt;index.js&lt;/code&gt;,项目入口&lt;/li&gt;
  &lt;li&gt;新建目录&lt;code class=&quot;highlighter-rouge&quot;&gt;controller&lt;/code&gt;,封装请求处理
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;account.js&lt;/code&gt; 与账户相关的处理&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;createAccount.js&lt;/code&gt;创建账户处理&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;transaction.js&lt;/code&gt; 与交易相关的处理&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;新建目录&lt;code class=&quot;highlighter-rouge&quot;&gt;model&lt;/code&gt;,模型封装
    * &lt;code class=&quot;highlighter-rouge&quot;&gt;responsedata.js&lt;/code&gt;,封装应答响应对象&lt;/li&gt;
  &lt;li&gt;新建目录&lt;code class=&quot;highlighter-rouge&quot;&gt;routers&lt;/code&gt;,路由处理
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;router.js&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;新建目录&lt;code class=&quot;highlighter-rouge&quot;&gt;utils&lt;/code&gt;，工具
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;myUtils.js&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;三响应对象&quot;&gt;三、响应对象&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;responsedata.js&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;function response(code, msg, data) {
    this.code = code;//状态码
    this.msg = msg;//状态描述
    this.data = data;//返回的数据
}
module.exports = response;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h2 id=&quot;四开启服务&quot;&gt;四、开启服务&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;index.js&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;const Koa = require(&quot;koa&quot;);
const app = new Koa();
const koaBody = require(&quot;koa-body&quot;);
const router = require(&quot;./routers/router&quot;);
app.use(async (ctx, next) =&amp;gt; {
    console.log(`Process ${ctx.request.method} ${ctx.request.url} ...`);
    await next();
});
//解析post请求
app.use(koaBody({
    multipart:true,
}));
app.use(router.routes());
app.listen(&quot;3000&quot;);
console.log(&quot;服务已开启,监听端口:3000&quot;);

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h2 id=&quot;五设置路由&quot;&gt;五、设置路由&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;router.js&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;const router = require(&quot;koa-router&quot;)();
const createAccountController = require(&quot;../controller/createAccount&quot;);
const accountController = require(&quot;../controller/account&quot;);
const transactionController = require(&quot;../controller/transaction&quot;);
//创建账号
router.post(&quot;/createaccount&quot;,createAccountController.createAccount);
//私钥解锁账户
router.post(&quot;/unlockaccountwithprivatekey&quot;,accountController.unlockAccountWithPrivateKey);
//keystore解锁账户
router.post(&quot;/unlockaccountwithkeystore&quot;,accountController.unlockAccountWithKeyStore);
//发起交易
router.post(&quot;/createtransaction&quot;,transactionController.createTransaction);
//确认交易
router.post(&quot;/sendtransaction&quot;,transactionController.sendTransaction);
//交易详情
router.post(&quot;/transactionstatus&quot;,transactionController.transactionStatus);
module.exports = router;

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;项目中所有请求都采用&lt;code class=&quot;highlighter-rouge&quot;&gt;post&lt;/code&gt;的方式&lt;/p&gt;

&lt;h2 id=&quot;六实例化web3&quot;&gt;六、实例化&lt;code class=&quot;highlighter-rouge&quot;&gt;web3&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;myUtils.js&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;const Web3 = require(&quot;web3&quot;);
var fs = require(&quot;fs&quot;);
getWeb3 = ()=&amp;gt;{
    //初始化web3访问节点为私有链节点
    const web3 = new Web3(Web3.givenProvider||&quot;http://localhost:8545&quot;);
    //以太坊测试链
    // const web3 = new Web3(Web3.givenProvider || &quot;https://kovan.infura.io/v3/4abf7f8865064ed6b99ca3fdac820921&quot;);
    return web3;
};
module.exports={
    getWeb3,
};
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;调用&lt;code class=&quot;highlighter-rouge&quot;&gt;getWeb3()&lt;/code&gt;获取&lt;code class=&quot;highlighter-rouge&quot;&gt;web3&lt;/code&gt;对象&lt;/p&gt;

&lt;blockquote&gt;
  &lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;var web3 = new Web3(Web3.givenProvider||&quot;http://localhost:8545&quot;);
&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
  &lt;li&gt;第一次调用&lt;code class=&quot;highlighter-rouge&quot;&gt;getWeb3()&lt;/code&gt;方法时&lt;code class=&quot;highlighter-rouge&quot;&gt;Web3.givenProvider&lt;/code&gt;为空,所以需要设置一个种子节点。&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;http://localhost:8545&lt;/code&gt;为本地私有链地址，&lt;a href=&quot;https://sweetmegan.github.io/2018/07/15/%E4%BB%A5%E5%A4%AA%E5%9D%8A%E7%A7%81%E7%BD%91%E5%BB%BA%E7%AB%8B-%E9%80%9A%E8%BF%87%E5%88%9B%E4%B8%96%E5%8C%BA%E5%9D%97%E6%9D%A5%E5%88%9D%E5%A7%8B%E5%8C%96%E5%8C%BA%E5%9D%97%E9%93%BE/&quot;&gt;怎么搭建私链&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;https://kovan.infura.io/v3/4abf7f8865064ed6b99ca3fdac820921&lt;/code&gt;是以太网&lt;code class=&quot;highlighter-rouge&quot;&gt;kovan&lt;/code&gt;测试链地址&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;获取测试链地址&quot;&gt;&lt;strong&gt;获取测试链地址&lt;/strong&gt;&lt;/h4&gt;

&lt;p&gt;打开 &lt;code class=&quot;highlighter-rouge&quot;&gt;INFURA&lt;/code&gt; https://infura.io/dashboard
注册，登录
登录成功后来到下面页面&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://otwxtrtn9.bkt.clouddn.com/%E8%8E%B7%E5%8F%96%E4%BB%A5%E5%A4%AA%E7%BD%91%E6%B5%8B%E8%AF%95%E9%93%BE%E5%9C%B0%E5%9D%80.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;可以看到主网和其他三个测试链，按需选择&lt;code class=&quot;highlighter-rouge&quot;&gt;ENDPOINT&lt;/code&gt;下的地址就是我们所需要的地址&lt;/p&gt;

&lt;h2 id=&quot;七请求处理&quot;&gt;七、请求处理&lt;/h2&gt;

&lt;h4 id=&quot;font-colorred在执行下面操作前需开启一个私链或者测试链font&quot;&gt;&amp;lt;font color=red&amp;gt;在执行下面操作前需开启一个私链或者测试链&amp;lt;/font&amp;gt;&lt;/h4&gt;

&lt;h4 id=&quot;1创建账户&quot;&gt;1、创建账户&lt;/h4&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;createAccount.js&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;const web3 = require(&quot;../utils/myUtils&quot;).getWeb3();
const path = require(&quot;path&quot;);
const fs = require(&quot;fs&quot;);
var respones = require(&quot;../model/responsedata&quot;);
//创建钱包
createAccount = async ctx =&amp;gt; {
    var responseData = new respones(0,&quot;success&quot;,{});
    // console.log(ctx.request.body);
    var body = ctx.request.body;
    //创建钱包
    var account = web3.eth.accounts.create();
    //生成keyStore文件
    //keyStore是将私钥与用户密码拼接,将拼接结果对称加密得到
    var keyStoreJson = account.encrypt(body.pwd);
    //保存keyStore
    //写入文件的keyStore数据
    // var keyStoreStr = JSON.stringify(keyStoreJson);
    // //keyStore文件名
    // var keyStoreFileName = &quot;UTC--&quot;+new Date().toISOString()+&quot;--&quot;+account.address;
    // //文件保存路径
    // var keyStoreFilePath = path.join(__dirname,&quot;../static/keystore&quot;,keyStoreFileName);
    // await fs.writeFile(keyStoreFilePath,keyStoreStr,()=&amp;gt;{});
    // var responseData = new respones(0,&quot;success&quot;,{
    //     &quot;downloadUrl&quot;:&quot;/keystore/&quot;+keyStoreFileName,
    //     &quot;privateKey&quot;:account.privateKey
    // });
    responseData.data.keyStore = keyStoreJson;
    ctx.body = responseData;
};
module.exports={
    createAccount
};

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;keystore&lt;/code&gt;文件在客户端自己保存，服务端不保存&lt;/p&gt;

&lt;h4 id=&quot;2账号操作&quot;&gt;2、账号操作&lt;/h4&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;account.js&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
const web3 = require(&quot;../utils/myUtils&quot;).getWeb3();
const myUtil = require(&quot;../utils/myUtils&quot;);
var response = require(&quot;../model/responsedata&quot;);
setResponseDataWithAccount = async(account, responseData)=&amp;gt; {
    responseData.data.address = account.address;
    responseData.data.privateKey = account.privateKey;
    responseData.data.balance = await getBalanceWithAddress(account.address);
    return responseData;
};

getBalanceWithAddress = async(address)=&amp;gt; {
    console.log(&quot;account:&quot; + address);
    var balance = await web3.eth.getBalance(address);
    return web3.utils.fromWei(balance, &quot;ether&quot;);
};
//使用私钥解锁账户
unlockAccountWithPrivateKey = async(ctx)=&amp;gt; {
    var responseData = new response(0, &quot;success&quot;, {});
    var body = ctx.request.body;
    var privateKey = body.privateKey;
    console.log(&quot;privateKey:&quot; + privateKey);
    var account = web3.eth.accounts.privateKeyToAccount(privateKey);
    // ctx.body = {name:&quot;解锁&quot;};
    var data = await setResponseDataWithAccount(account, responseData);
    console.log(data);
    ctx.body = data;
};
//使用keyStore文件
unlockAccountWithKeyStore = async(ctx)=&amp;gt; {
    var responseData = new response(0, &quot;success&quot;, {});
    var body = ctx.request.body;
    var pwd = body.pwd;
    var keyStore = body.keyStore;
    try {
        var account = web3.eth.accounts.decrypt(keyStore, pwd);
        ctx.body = await setResponseDataWithAccount(account, responseData);
    }catch (error){
        console.log(error)
        responseData.code = 1;
        responseData.message = &quot;failed&quot;;
        responseData.data = {error: error.message};
        ctx.body = responseData;
    }
};
module.exports = {
    unlockAccountWithPrivateKey,
    unlockAccountWithKeyStore,
};

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h4 id=&quot;3交易处理&quot;&gt;3、交易处理&lt;/h4&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;transaction.js&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;const web3 = require(&quot;../utils/myUtils&quot;).getWeb3();
var response = require(&quot;../model/responsedata&quot;);
var bignumber = require(&quot;bignumber.js&quot;)

//发起交易
createTransaction = async(ctx)=&amp;gt; {
    console.log(&quot;createTransaction&quot;);
    var responseData = new response(0, &quot;success&quot;, {});
    var body = ctx.request.body;
    var fromAddress = body.from;
    var toAddress = body.to;
    //将输入的金额换算成Wei
    var money = web3.utils.toWei(body.money, &quot;ether&quot;);
    var gasPrice = await web3.eth.getGasPrice();
    //获取交易的nonce值,一个顺序累加的值
    var nonce = await web3.eth.getTransactionCount(fromAddress);
    var transactionData = {
        from: fromAddress,
        to: toAddress,
        value: money,
        gasPrice: gasPrice,
        data: '0x00',//当使用代币转账或者合约调用时
        nonce: nonce,
    };
    //estimateGas()方法会将transactionData数据做一些操作,导致,transactionData一些值的类型变化,所以下面对transactionData重新赋值
    var gas = await web3.eth.estimateGas(transactionData);
    transactionData = {
        from: fromAddress,
        to: toAddress,
        value: money,
        gasPrice: gasPrice,
        data: '0x00',//当使用代币转账或者合约调用时
        nonce: nonce,
        gas:gas,
    };
    console.log(transactionData);
    responseData.data = transactionData;
    ctx.body = responseData;

};

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;创建一笔交易，返回交易的&lt;code class=&quot;highlighter-rouge&quot;&gt;gas&lt;/code&gt;和&lt;code class=&quot;highlighter-rouge&quot;&gt;gasPrice&lt;/code&gt;供用户确认&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;//确认交易
/*
* 1、签名交易
* 2、发送交易
* */
sendTransaction = async(ctx)=&amp;gt; {

    console.log(&quot;sendTransaction&quot;);

    var responseData = new response(0, &quot;success&quot;, {});
    var body = ctx.request.body;
    var transactionData = {
        from: body.from,
        to: body.to,
        value: body.value,
        gasPrice: body.gasPrice,
        data: body.data,//当使用代币转账或者合约调用时
        nonce: body.nonce,
        gas: body.gas
    };
    //privateKey为了安全,需要进行加密处理
    var privateKey = body.privateKey;
    //签名交易
    console.log(transactionData);
    console.log(privateKey);
    var signTransactionData = await web3.eth.accounts.signTransaction(transactionData, privateKey);
    try {
        //发送交易
        await web3.eth.sendSignedTransaction(signTransactionData.rawTransaction, (error, hash)=&amp;gt; {
            if (!error) {
                responseData.data.hash = hash;
            } else {
                responseData.code = 1;
                responseData.message = &quot;failed&quot;;
                responseData.data = {error: error.message};
            }
        })
    } catch (error) {
        console.log(error);
        responseData.code = 1;
        responseData.message = &quot;failed&quot;;
        responseData.data = {error: error.message};
    }
    ctx.body = responseData
};
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;确认一笔交易，这里才是真正提交一笔交易，这时不会直接返回结果，交易提交后，如果连接的是测试链，需要自己去挖矿,执行命令&lt;code class=&quot;highlighter-rouge&quot;&gt;miner.start()&lt;/code&gt;,直到矿工确认这笔交易后，才会返回交易信息。如果是测试链，因为它有自己的生态，不需要做其他操作。&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;//根据txHash查询交易状态
transactionStatus = async(ctx)=&amp;gt; {
  var responseData = new response(0,&quot;success&quot;,{});
    var data = ctx.request.body;
    var txHash = data.txHash;
    var result = await web3.eth.getTransactionReceipt(txHash);
    if (result != null){
        responseData.data = result;
    }
    ctx.body = responseData;
};
module.exports = {
    createTransaction,
    sendTransaction,
    transactionStatus,
};

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;根据交易hash，返回&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;注 ：&lt;code class=&quot;highlighter-rouge&quot;&gt;createTransaction&lt;/code&gt;方法中调用&lt;code class=&quot;highlighter-rouge&quot;&gt;estimateGas()&lt;/code&gt;方法会将&lt;code class=&quot;highlighter-rouge&quot;&gt;transactionData&lt;/code&gt;数据做一些操作,导致,&lt;code class=&quot;highlighter-rouge&quot;&gt;transactionData&lt;/code&gt;一些值的类型变化,所以下面对&lt;code class=&quot;highlighter-rouge&quot;&gt;transactionData&lt;/code&gt;重新赋值&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;demo下载-httpsgithubcomsweetmeganmywalletdemo&quot;&gt;demo下载 https://github.com/sweetMegan/myWalletDemo&lt;/h2&gt;

&lt;h4 id=&quot;mywallet为nodejs后台&quot;&gt;&lt;strong&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;myWallet&lt;/code&gt;为nodejs后台&lt;/strong&gt;&lt;/h4&gt;
&lt;h4 id=&quot;mywalletclient为ios前端&quot;&gt;&lt;strong&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;myWalletClient&lt;/code&gt;为iOS前端&lt;/strong&gt;&lt;/h4&gt;

</description>
        <pubDate>Sun, 15 Jul 2018 00:00:00 +0800</pubDate>
        <link>/2018/07/15/Nodejs%E5%AE%9E%E7%8E%B0%E4%BB%A5%E5%A4%AA%E5%9D%8A%E9%92%B1%E5%8C%85-iOS%E5%AE%9E%E7%8E%B0%E5%89%8D%E7%AB%AF%E8%B0%83%E7%94%A8/</link>
        <guid isPermaLink="true">/2018/07/15/Nodejs%E5%AE%9E%E7%8E%B0%E4%BB%A5%E5%A4%AA%E5%9D%8A%E9%92%B1%E5%8C%85-iOS%E5%AE%9E%E7%8E%B0%E5%89%8D%E7%AB%AF%E8%B0%83%E7%94%A8/</guid>
        
        <category>区块链</category>
        
        
      </item>
    
      <item>
        <title>Nodejs框架Koa</title>
        <description>&lt;h2 id=&quot;安装环境&quot;&gt;安装环境&lt;/h2&gt;

&lt;h3 id=&quot;安装node与npm&quot;&gt;安装Node与npm&lt;/h3&gt;

&lt;h4 id=&quot;安装nodejs&quot;&gt;安装Node.js&lt;/h4&gt;

&lt;blockquote&gt;
  &lt;p&gt;brew install node@8&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4 id=&quot;安装指定版本的npm&quot;&gt;安装指定版本的npm&lt;/h4&gt;

&lt;blockquote&gt;
  &lt;p&gt;npm install npm@5.6.0 -g&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4 id=&quot;检查node版本&quot;&gt;检查Node版本&lt;/h4&gt;

&lt;blockquote&gt;
  &lt;p&gt;node -v&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4 id=&quot;检查npm版本&quot;&gt;检查npm版本&lt;/h4&gt;

&lt;blockquote&gt;
  &lt;p&gt;npm -v&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4 id=&quot;升级node&quot;&gt;升级Node&lt;/h4&gt;

&lt;blockquote&gt;
  &lt;p&gt;npm install -g n&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;一koa&quot;&gt;一、Koa&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;安装Koa&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;新建一个项目&lt;code class=&quot;highlighter-rouge&quot;&gt;koademo&lt;/code&gt;，打开终端&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;mkdir koademo
cd  koademo
npm init&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;此时会要求输入一些配置信息，什么都不用写，一直回车&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://otwxtrtn9.bkt.clouddn.com/Nodejs%E6%A1%86%E6%9E%B6Koa%202.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;此时项目路径下会新增文件&lt;code class=&quot;highlighter-rouge&quot;&gt;package.json&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;安装&lt;code class=&quot;highlighter-rouge&quot;&gt;koa&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;npm install koa&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;新建&lt;code class=&quot;highlighter-rouge&quot;&gt;index.js&lt;/code&gt;文件，添加如下代码,开启服务,监听端口3000&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;const Koa = require(&quot;koa&quot;);
const app = new Koa();
//开启服务,监听端口3000
app.listen(3000);

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;new Koa():创建的对象被称为&lt;code class=&quot;highlighter-rouge&quot;&gt;Koa&lt;/code&gt;应用对象。应用对象时带有&lt;code class=&quot;highlighter-rouge&quot;&gt;node http&lt;/code&gt;服务的Koa接口，它可以处理中间件的注册，将&lt;code class=&quot;highlighter-rouge&quot;&gt;http&lt;/code&gt;请求分发到中间件，进行默认错误处理，以及对上下文，请求和响应对象进行配置。
app.listen(3000):用于启动一个服务的快捷方法，是对&lt;code class=&quot;highlighter-rouge&quot;&gt;http.createServer&lt;/code&gt;的简单包装，它实际上这样运行:&lt;code class=&quot;highlighter-rouge&quot;&gt;http:createServer(app.callback()).listen(3000)&lt;/code&gt;;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;context&quot;&gt;context&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;context&lt;/code&gt;是一个请求的上下文，该对象封装了一个传入的&lt;code class=&quot;highlighter-rouge&quot;&gt;http&lt;/code&gt;消息，&lt;code class=&quot;highlighter-rouge&quot;&gt;context&lt;/code&gt;有&lt;code class=&quot;highlighter-rouge&quot;&gt;request&lt;/code&gt;和&lt;code class=&quot;highlighter-rouge&quot;&gt;response&lt;/code&gt;属性，我们可以设置两个属性来处理和响应不同的请求。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;代码&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;const Koa = require(&quot;koa&quot;);
const app = new Koa();
//开启服务,监听端口3000
app.use((ctx,next)=&amp;gt;{
   ctx.response.body=&quot;hello world&quot;;
});
app.listen(3000);

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;ul&gt;
  &lt;li&gt;说明&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;app.use(function):将给定的function当做中间件加载到应用中。
ctx:每一个请求都会创建一段上下文，在控制业务逻辑的中间件中，上下文被寄存在&lt;code class=&quot;highlighter-rouge&quot;&gt;ctx&lt;/code&gt;对象中。为了使用方便，许多上下文属性和方法都被委托代理到他们的&lt;code class=&quot;highlighter-rouge&quot;&gt;ctx.request&lt;/code&gt;和&lt;code class=&quot;highlighter-rouge&quot;&gt;ctx.response&lt;/code&gt;，比如访问&lt;code class=&quot;highlighter-rouge&quot;&gt;ctx.type&lt;/code&gt;和&lt;code class=&quot;highlighter-rouge&quot;&gt;ctx.length&lt;/code&gt;将被代理到&lt;code class=&quot;highlighter-rouge&quot;&gt;response&lt;/code&gt;对象,&lt;code class=&quot;highlighter-rouge&quot;&gt;ctx.path&lt;/code&gt;和&lt;code class=&quot;highlighter-rouge&quot;&gt;ctx.method&lt;/code&gt;将被代理到&lt;code class=&quot;highlighter-rouge&quot;&gt;request&lt;/code&gt;对象。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
  &lt;li&gt;启动服务&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;node index.js&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;浏览器访问 &lt;code class=&quot;highlighter-rouge&quot;&gt;http://localhost:3000&lt;/code&gt;,如果运行正确，在页面会显示&lt;code class=&quot;highlighter-rouge&quot;&gt;hello world&lt;/code&gt;。&lt;/p&gt;

&lt;h2 id=&quot;三网页模板&quot;&gt;三、网页模板&lt;/h2&gt;

&lt;p&gt;实际开发中，返回给用户的网页往往都写成模板文件。我们可以让&lt;code class=&quot;highlighter-rouge&quot;&gt;Koa&lt;/code&gt;先读取模板文件,然后将这个模板返回给用户。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;代码&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; const Koa = require(&quot;koa&quot;);
const app = new Koa();
const fs = require(&quot;fs&quot;);
app.use((ctx,next)=&amp;gt;{
    //必须指定type,否则调用fs模板后默认响应类型为为`application/octet-stream`类型
    ctx.response.type=&quot;text/html&quot;;
    //将文件作为响应体流失传输
    ctx.response.body=fs.createReadStream(&quot;./views/index.html&quot;);
});
app.listen(&quot;3000&quot;);

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h2 id=&quot;四中间件&quot;&gt;四、中间件&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Koa&lt;/code&gt;所有的功能都是通过中间件实现的。中间件处在&lt;code class=&quot;highlighter-rouge&quot;&gt;HTTP Request&lt;/code&gt;和&lt;code class=&quot;highlighter-rouge&quot;&gt;HTTP Response&lt;/code&gt;中间&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Koa&lt;/code&gt;的中间件之间按照编码顺序在栈内一次执行，允许您执行操作并向下传递请求，之后过滤并逆序返回响应。&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;const Koa = require('koa');
const app = new Koa();

app.use(async (ctx,next)=&amp;gt;{
    console.log(&quot;first&quot;);
    await next();
    console.log(&quot;first over&quot;)
});
app.use(async(ctx,next)=&amp;gt;{
   console.log(&quot;second&quot;);
    await next();
    console.log(&quot;second over&quot;)

});
app.use(async(ctx,next)=&amp;gt;{
    console.log(&quot;third&quot;);
    await next();
console.log(&quot;third over&quot;);
});

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;有&lt;code class=&quot;highlighter-rouge&quot;&gt;async&lt;/code&gt;标记的函数称为异步函数，在异步函数中，可以用&lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt;调用另一个异步函数。使用&lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt;时，它所在的方法必须使用关键字async。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;执行结果:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;first&lt;/p&gt;

  &lt;p&gt;second&lt;/p&gt;

  &lt;p&gt;third&lt;/p&gt;

  &lt;p&gt;third over&lt;/p&gt;

  &lt;p&gt;second over&lt;/p&gt;

  &lt;p&gt;first over&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;五原生路由&quot;&gt;五、原生路由&lt;/h2&gt;

&lt;p&gt;开发中需要根据用户请求返回相应的数据，可以通过设置路由实现&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;代码&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;const Koa = require(&quot;koa&quot;);
const app = new Koa();
app.use(async (ctx)=&amp;gt;{
    if (ctx.request.path==&quot;/&quot;){
        ctx.response.body = &quot;首页&quot;;
    }
    switch (ctx.request.path){
        case &quot;/&quot;:{
            ctx.response.body = &quot;首页&quot;;
            break;
        }
        case &quot;/login&quot;:{
            ctx.body = &quot;login&quot;;
            break;
        }
        case &quot;/test&quot;:{
            ctx.body = &quot;test&quot;;
            break;
        }
        
    }
});
//开启服务,监听端口3000
app.listen(3000);

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;浏览器访问&lt;/th&gt;
      &lt;th&gt;结果&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;http://localhost:3000/&lt;/td&gt;
      &lt;td&gt;首页&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;http://localhost:3000/login&lt;/td&gt;
      &lt;td&gt;login&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;http://localhost:3000/test&lt;/td&gt;
      &lt;td&gt;test&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2 id=&quot;六koa-router&quot;&gt;六、koa-router&lt;/h2&gt;

&lt;p&gt;原生路由用起来不方便，我们可以使用封装好的&lt;a href=&quot;https://www.npmjs.com/package/koa-route&quot;&gt;koa-router&lt;/a&gt;模块&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;npm install koa-router&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
  &lt;li&gt;代码&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;const Koa = require(&quot;koa&quot;);
const app = new Koa();
const r = require(&quot;koa-router&quot;);
const router = r();
//设置路由
router.get(&quot;/&quot;,(ctx,next)=&amp;gt;{
    ctx.body = &quot;首页&quot;;

});
router.get(&quot;/login&quot;,(ctx,next)=&amp;gt;{
    ctx.body = &quot;login&quot;;

});
router.get(&quot;/test&quot;,(ctx,next)=&amp;gt;{
    ctx.body = &quot;test&quot;;
});
//绑定路由
app.use(router.routes());
//开启服务,监听端口3000
app.listen(3000);

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h2 id=&quot;七重定向&quot;&gt;七、重定向&lt;/h2&gt;

&lt;p&gt;将上面的代码中&lt;code class=&quot;highlighter-rouge&quot;&gt;test&lt;/code&gt;重定向到&lt;code class=&quot;highlighter-rouge&quot;&gt;login&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;const Koa = require(&quot;koa&quot;);
const app = new Koa();
const r = require(&quot;koa-router&quot;);
const router = r();
//设置路由
router.get(&quot;/&quot;,(ctx,next)=&amp;gt;{
    ctx.body = &quot;首页&quot;;

});
router.get(&quot;/login&quot;,(ctx,next)=&amp;gt;{
    ctx.body = &quot;login&quot;;

});
//test重定向到login
router.get(&quot;/test&quot;,(ctx,next)=&amp;gt;{
    ctx.response.redirect(&quot;/login&quot;);
});
//绑定路由
app.use(router.routes());
//开启服务,监听端口3000
app.listen(3000);

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h2 id=&quot;八获取get请求参数&quot;&gt;八、获取get请求参数&lt;/h2&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/**
 * Created by zhqmac on 2018/7/22.
 */
const Koa = require(&quot;koa&quot;);
const app = new Koa();
const router= require(&quot;koa-router&quot;)();
app.use((ctx,next)=&amp;gt;{
   console.log(ctx.request.method);
    console.log(ctx.request.url);
    next()
});
//http://localhost:3000/login?name=zhq?age=26
router.get(&quot;/login&quot;,ctx=&amp;gt;{
   var name = ctx.query.name;
    var age = ctx.query.age;
    console.log(name+&quot;年龄:&quot;+age);
    ctx.body = `&amp;lt;h1&amp;gt; Hello,${name}&amp;lt;/h1&amp;gt;`
});
//http://localhost:3000/regist/zhq/26
router.get(&quot;/regist/:name/:age&quot;,ctx=&amp;gt;{
    var name = ctx.params.name;
    var age = ctx.params.age;
    ctx.response.body = `&amp;lt;h1&amp;gt;regist, ${name} ${age}!&amp;lt;/h1&amp;gt;`;


});
app.use(router.routes());
//开启服务,监听端口3000
app.listen(3000);

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h2 id=&quot;获取post请求参数&quot;&gt;获取post请求参数&lt;/h2&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;const Koa = require(&quot;koa&quot;);
const app = new Koa();
const koaBody = require(&quot;koa-body&quot;);

const router= require(&quot;koa-router&quot;)();
app.use(async (ctx,next)=&amp;gt;{
    console.log(ctx.request.method);
    console.log(ctx.request.url);
    await next()
});
router.post(&quot;/login&quot;, (ctx,next) =&amp;gt; {
    var body = ctx.request.body;
    console.log(body);
    console.log(body.name);
    ctx.body = {name:body.name};

});
app.use(koaBody());
app.use(router.routes());
//开启服务,监听端口3000
app.listen(3000);

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;使用&lt;code class=&quot;highlighter-rouge&quot;&gt;curl&lt;/code&gt;发起post请求&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;curl -X POST –data “name=zhq” 127.0.0.1:3000/login&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;返回结果是一个json串&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;zhq&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id=&quot;十加载静态资源&quot;&gt;十、加载静态资源&lt;/h2&gt;

&lt;p&gt;如图片、字体、样式表、脚本等&lt;/p&gt;

&lt;p&gt;引入&lt;code class=&quot;highlighter-rouge&quot;&gt;koa-static&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;npm install koa-static&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;下面以加载一张图片为例&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;新建目录&lt;code class=&quot;highlighter-rouge&quot;&gt;static&lt;/code&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;在&lt;code class=&quot;highlighter-rouge&quot;&gt;static&lt;/code&gt;目录下新建&lt;code class=&quot;highlighter-rouge&quot;&gt;images&lt;/code&gt;目录&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;在&lt;code class=&quot;highlighter-rouge&quot;&gt;images&lt;/code&gt;目录下添加一张图片&lt;code class=&quot;highlighter-rouge&quot;&gt;avatar.JPG&lt;/code&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
  &lt;li&gt;代码&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;const Koa = require(&quot;koa&quot;);
const app = new Koa();
const path = require(&quot;path&quot;);
const koaStatic= require(&quot;koa-static&quot;);
const router= require(&quot;koa-router&quot;)();

//静态资源目录对于相对入口文件的路径
const staticPath = './static';

router.get(&quot;/&quot;,ctx=&amp;gt;{
   ctx.body = &quot;&lt;span class=&quot;nt&quot;&gt;&amp;lt;html&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'/images/avatar.JPG'&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;点我&lt;span class=&quot;nt&quot;&gt;&amp;lt;/a&amp;gt;&amp;lt;/html&amp;gt;&lt;/span&gt;&quot;
});
app.use(koaStatic(
    path.join(__dirname,staticPath)
));
app.use(router.routes());
//开启服务,监听端口3000
app.listen(3000);

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h2 id=&quot;十一模板引擎&quot;&gt;十一、模板引擎&lt;/h2&gt;

&lt;p&gt;安装ejs模板引擎&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;npm install ejs
npm install koa-views&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
  &lt;li&gt;代码&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;const Koa = require(&quot;koa&quot;);
const app = new Koa();
//引入koa-router的时候要加上一个()
var router = require(&quot;koa-router&quot;)();
var views = require(&quot;koa-views&quot;);
var path = require(&quot;path&quot;);
//views 必须是异步才可以
router.get(&quot;/login&quot;,async ctx=&amp;gt;{
    //将json里面的值替换为文件里面的变量
    //go 里面使用的是template模板,他们功能类型
    var name = &quot;zhq&quot;;
    await ctx.render(&quot;loginEjs.ejs&quot;,{
        name
    })
});
router.get(&quot;/regist&quot;,async ctx=&amp;gt;{
    await ctx.render(&quot;regist.html&quot;)
});
app.use(views(
    path.join(__dirname,&quot;./views&quot;),
    {
        extensions:&quot;ejs&quot;,
        //默认去views下面获取ejs后缀的文件
        //如果是其他类型的文件需要制定文件类型
        map:{
            html:&quot;ejs&quot;
        }
    }
));
//路由注册到中间件
app.use(router.routes());
//开启服务,监听端口3000
app.listen(3000);

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;loginEjs.js&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;html&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;lang=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;en&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;meta&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;charset=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;UTF-8&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Title&lt;span class=&quot;nt&quot;&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nl&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;darkcyan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;div&amp;gt;&lt;/span&gt;注册  &lt;span class=&quot;err&quot;&gt;&amp;lt;&lt;/span&gt;%=  name %&amp;gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h2 id=&quot;源码下载-httpsgithubcomsweetmegankoademo&quot;&gt;源码下载 https://github.com/sweetMegan/koaDemo&lt;/h2&gt;

&lt;h2 id=&quot;感谢李旭旭哥指导&quot;&gt;感谢李旭（旭哥）指导&lt;/h2&gt;

</description>
        <pubDate>Sat, 14 Jul 2018 00:00:00 +0800</pubDate>
        <link>/2018/07/14/Nodejs%E6%A1%86%E6%9E%B6Koa/</link>
        <guid isPermaLink="true">/2018/07/14/Nodejs%E6%A1%86%E6%9E%B6Koa/</guid>
        
        <category>区块链</category>
        
        
      </item>
    
      <item>
        <title>Nodejs实现以太坊钱包-概述</title>
        <description>&lt;p&gt;􏰲􏰳􏰴􏰩􏰵􏰶􏰷􏰸􏰹􏰬􏰺􏰻􏰼􏰽􏰾􏰲􏰳􏰴􏰩􏰵􏰶􏰷􏰸􏰹􏰬􏰺􏰻􏰼􏰽􏰾􏰲􏰳􏰴􏰩􏰵􏰶􏰷􏰸􏰹􏰬􏰺􏰻􏰼􏰽􏰾􏰲􏰳􏰴􏰩􏰵􏰶􏰷􏰸􏰹􏰬
如何做一个功能完整的以太坊钱包？以太坊官方提供了一些API()给开发者使用，但是依靠这些API不足以做成一个完整功能的钱包。主要在转账这块，由于安全性考虑，在加解密，数字签名这块不可能有现成的API暴露给开发者。官方给了一个Nodejs的模块给我们使用，叫web3。&lt;/p&gt;

&lt;h4 id=&quot;web3&quot;&gt;&lt;strong&gt;web3&lt;/strong&gt;&lt;/h4&gt;

&lt;p&gt;web3模块主要连接以太坊暴露出来的RPC层。开发者利用web3连接RPC层，可以连接任何暴露了RPC接口的节点，从而与区块链交互。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;web3&lt;/code&gt;中有&lt;code class=&quot;highlighter-rouge&quot;&gt;eth&lt;/code&gt;对象-&lt;code class=&quot;highlighter-rouge&quot;&gt;web.eth&lt;/code&gt;具体来表示与以太坊区块链和智能合约之间的交互。&lt;code class=&quot;highlighter-rouge&quot;&gt;web3.util&lt;/code&gt;有一些辅助函数。另外还有shh和bzz用于通信和文件存储的对象。&lt;/p&gt;

&lt;p&gt;github地址：&lt;a href=&quot;https://github.com/ethereum/web3.js/tree/v1.0.0-beta.34&quot;&gt;web3&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;文档:&lt;a href=&quot;http://web3js.readthedocs.io/en/1.0/&quot;&gt;web3&lt;/a&gt;&lt;/p&gt;

&lt;h4 id=&quot;koa&quot;&gt;&lt;strong&gt;Koa&lt;/strong&gt;&lt;/h4&gt;

&lt;p&gt;我们的钱包项目使用web3可以与区块链交互了，这个web3是Nodejs的库，那么我们就用nodejs提供后台接口供前端调用即可，这里使用Koa框架。&lt;/p&gt;

&lt;p&gt;Koa是富有表现力的HTTP中间件框架，使web应用程序和API更易于编写，他的特点优雅、简洁、表达力强、自由度高。&lt;/p&gt;

&lt;p&gt;github地址：&lt;a href=&quot;https://github.com/koajs/koa/tree/2.5.2&quot;&gt;Koa&lt;/a&gt;&lt;/p&gt;

&lt;h4 id=&quot;中间件&quot;&gt;&lt;strong&gt;中间件&lt;/strong&gt;&lt;/h4&gt;

&lt;p&gt;Koa的最大特色，也是最重要的一个设计，就是中间件。Koa中间件是简单的函数，调用&lt;code class=&quot;highlighter-rouge&quot;&gt;app.use()&lt;/code&gt;传入&lt;code class=&quot;highlighter-rouge&quot;&gt;MiddlewareFunction&lt;/code&gt;函数带有两个参数&lt;code class=&quot;highlighter-rouge&quot;&gt;(ctx,next)&lt;/code&gt;。中间件处在&lt;code class=&quot;highlighter-rouge&quot;&gt;HTTP Request&lt;/code&gt;和&lt;code class=&quot;highlighter-rouge&quot;&gt;HTTP Response&lt;/code&gt;之间，用来实现某种中间功能。如:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;app.use(async (ctx, next) =&amp;gt; {
    console.log(`Process ${ctx.request.method} ${ctx.request.url} ...`);
    await next();
});

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ctx&lt;/code&gt; 是一个请求的上下文，该对象封装了一个传入的http消息，并对该消息进行了相应的响应。Koa提供了一个&lt;code class=&quot;highlighter-rouge&quot;&gt;Request&lt;/code&gt;对象作为&lt;code class=&quot;highlighter-rouge&quot;&gt;Context&lt;/code&gt;和&lt;code class=&quot;highlighter-rouge&quot;&gt;request&lt;/code&gt;属性。&lt;code class=&quot;highlighter-rouge&quot;&gt;Koa&lt;/code&gt;的&lt;code class=&quot;highlighter-rouge&quot;&gt;Request&lt;/code&gt;对象提供了用于处理&lt;code class=&quot;highlighter-rouge&quot;&gt;http&lt;/code&gt;请求的方法。&lt;code class=&quot;highlighter-rouge&quot;&gt;Koa&lt;/code&gt;提供了一个&lt;code class=&quot;highlighter-rouge&quot;&gt;Response&lt;/code&gt;对象作为&lt;code class=&quot;highlighter-rouge&quot;&gt;Context&lt;/code&gt;和&lt;code class=&quot;highlighter-rouge&quot;&gt;response&lt;/code&gt;属性。&lt;code class=&quot;highlighter-rouge&quot;&gt;Koa&lt;/code&gt;的&lt;code class=&quot;highlighter-rouge&quot;&gt;Response&lt;/code&gt;对象提供了用于处理&lt;code class=&quot;highlighter-rouge&quot;&gt;http&lt;/code&gt;响应的方法。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;next&lt;/code&gt;是一个被调用来执行下游中间件的函数，必须手动调用&lt;code class=&quot;highlighter-rouge&quot;&gt;next()&lt;/code&gt;以运行下游中间件。&lt;/p&gt;

&lt;h2 id=&quot;感谢李旭旭哥指导&quot;&gt;&lt;strong&gt;感谢李旭（旭哥）指导&lt;/strong&gt;&lt;/h2&gt;

</description>
        <pubDate>Sat, 14 Jul 2018 00:00:00 +0800</pubDate>
        <link>/2018/07/14/Nodejs%E5%AE%9E%E7%8E%B0%E4%BB%A5%E5%A4%AA%E5%9D%8A%E9%92%B1%E5%8C%85-%E6%A6%82%E8%BF%B0/</link>
        <guid isPermaLink="true">/2018/07/14/Nodejs%E5%AE%9E%E7%8E%B0%E4%BB%A5%E5%A4%AA%E5%9D%8A%E9%92%B1%E5%8C%85-%E6%A6%82%E8%BF%B0/</guid>
        
        <category>区块链</category>
        
        
      </item>
    
  </channel>
</rss>
